From 84fe1605911cabc7eab9d0a1f7384b6778c8eba5 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Mon, 15 Oct 2018 13:45:21 +0200
Subject: [PATCH 1/8] Cleanup and standardization of collection functions

- collection functions now always return a collection, also when callback functions are used
- if a callback function returns false and aborts the loop, the collection up to this point is returned as an array
- additional containers are allowed now for text collections and for items()
- got rid of private function forEachTextCollection() as it was not needed anymore
- rewriting of the collection functions docs, to make the functions easier to understand
---
 basil.js                 | 136 +++++++++++++++++++--------------------
 src/includes/data.js     |   7 +-
 src/includes/document.js | 129 +++++++++++++++++--------------------
 3 files changed, 130 insertions(+), 142 deletions(-)

diff --git a/basil.js b/basil.js
index 0f2e6492..4d11efbf 100644
--- a/basil.js
+++ b/basil.js
@@ -1524,10 +1524,13 @@ forEach = function(collection, cb) {
     }
 
     if(cb(collection[i], i) === false) {
-      return false;
+      if(collection.hasOwnProperty("everyItem")) {
+        return collection.everyItem().getElements().slice(0, i);
+      }
+      return collection.slice(0, i);
     }
   }
-  return true;
+  return collection;
 };
 
 // ----------------------------------------
@@ -3463,31 +3466,28 @@ pub.group = function (pItems, name) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of items otherwise calls the given callback function for each of the PageItems in the given Document, Page, Layer or Group.
+ * @description Returns a collection of all page items in the given container. The container object can be a Document, Page, Layer, Group, Story, Page Item or Text Object.
+ * If a callback function is given, `items()` calls this callback function on each page item of the given container. When the callback function returns false, the loop stops and the `items()` function returns an array of all page items that did not return false.
  *
  * @cat     Document
  * @subcat  Page Items
  * @method  items
  *
- * @param   {Document|Page|Layer|Group} container The container where the PageItems sit in
- * @param   {Function|Boolean} [cb] Optional: The callback function to call for each PageItem. When this function returns false the loop stops. Passed arguments: `item`, `loopCount`.
- * @return  {PageItems} A collection of PageItem objects.
+ * @param   {Document|Page|Layer|Group|Story|PageItem|TextObject} container The document, page, layer, group, story, page item or text object instance to iterate the page items in.
+ * @param   {Function} [cb] Optional: The callback function to call with each page item. When this function returns false the loop stops. Passed arguments: `item`, `loopCount`
+ * @return  {PageItems|Array} A collection or an array of page items.
  */
 pub.items = function(container, cb) {
 
-  if (container instanceof Document
-    || container instanceof Page
-    || container instanceof Layer
-    || container instanceof Group) {
+  if (arguments.length && container.hasOwnProperty("allPageItems")) {
 
-    if(arguments.length === 1 || cb === false) {
-      return container.allPageItems;
-    } else if(cb instanceof Function) {
+    if(cb instanceof Function) {
       return forEach(container.allPageItems, cb);
     }
+    return container.allPageItems;
   }
-  error("items(), Not a valid PageItem container, should be Document, Page, Layer or Group");
-  return null;
+
+  error("items(), Not a valid PageItem container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
 };
 
 /**
@@ -3745,15 +3745,16 @@ pub.addToStory = function(story, itemOrString, insertionPointorMode) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of characters in the container otherwise calls the given callback function with each character of the given document, page, story, textFrame, paragraph, line or word.
+ * @description Returns a collection of all character objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph, Line or Word.
+ * If a callback function is given, `characters()` calls this callback function on each character object of the given container. When the callback function returns false, the loop stops and the `characters()` function returns an array of all characters that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  characters
  *
- * @param   {Document|Page|Story|TextFrame|Paragraph|Line|Word} container The document, page, story, textFrame, paragraph, line or word instance to  iterate the characters in.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame, paragraph, line or word instance to  iterate the characters in.
  * @param   {Function} [cb] Optional: The callback function to call with each character. When this function returns false the loop stops. Passed arguments: `character`, `loopCount`
- * @return  {Characters} A collection of Character objects.
+ * @return  {Characters|Array} A collection or an array of Character objects.
  */
 pub.characters = function(container, cb) {
 
@@ -3763,16 +3764,18 @@ pub.characters = function(container, cb) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of lines in the container otherwise calls the given callback function with each line of the given document, page, story, textFrame or paragraph.
+ * @description Returns a collection of all line objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame or Paragraph. Please note that `lines()` refers to lines of text in a text frame. If you need to construct a geometric line on a page, use `line()` instead.
+ * If a callback function is given, `lines()` calls this callback function on each line object of the given container. When the callback function returns false, the loop stops and the `lines()` function returns an array of all lines that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  lines
  *
- * @param   {Document|Page|Story|TextFrame|Paragraph} container The document, page, story, textFrame or paragraph instance to iterate the lines in.
- * @param   {Function} [cb] Optional: The callback function to call with each line. When this function returns false the loop stops. Passed arguments: `line`, `loopCount`.
- * @return  {Lines} A collection of Line objects.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame or paragraph instance to  iterate the lines in.
+ * @param   {Function} [cb] Optional: The callback function to call with each line. When this function returns false the loop stops. Passed arguments: `line`, `loopCount`
+ * @return  {Lines|Array} A collection or an array of Line objects.
  */
+
 pub.lines = function(container, cb) {
 
   var legalContainers = "Document, Story, Page, TextFrame or Paragraph.";
@@ -3799,15 +3802,16 @@ pub.linkTextFrames = function (textFrameA, textFrameB) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of paragraphs in the container otherwise calls the given callback function with each paragraph of the given document, page, story or textFrame.
+ * @description Returns a collection of all paragraph objects in the given container. The container object can be a Document, Page, Layer, Group, Story or Text Frame.
+ * If a callback function is given, `paragraphs()` calls this callback function on each paragraph object of the given container. When the callback function returns false, the loop stops and the `paragraphs()` function returns an array of all paragraphs that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  paragraphs
  *
- * @param   {Document|Page|Story|TextFrame} container The document, story, page or textFrame instance to iterate the paragraphs in.
- * @param   {Function} [cb] Optional: The callback function to call with each paragraph. When this function returns false the loop stops. Passed arguments: `para`, `loopCount`.
- * @return  {Paragraphs} A collection of Paragraph objects.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame} container The document, page, layer, group, story or textFrame instance to  iterate the paragraphs in.
+ * @param   {Function} [cb] Optional: The callback function to call with each paragraph. When this function returns false the loop stops. Passed arguments: `paragraph`, `loopCount`
+ * @return  {Paragraphs|Array} A collection or an array of Paragraph objects.
  */
 pub.paragraphs = function(container, cb) {
 
@@ -3838,15 +3842,16 @@ pub.placeholder = function (textFrame) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of items otherwise calls the given callback function with each story of the given document.
+ * @description Returns a collection of all story objects in the given document.
+ * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  stories
  *
- * @param   {Document} doc The document instance to iterate the stories in
- * @param   {Function} [cb] The callback function to call with each story. When this function returns `false` the loop stops. Passed arguments: `story`, `loopCount`.
- * @return  {Stories} A collection of Story objects.
+ * @param   {Document} doc The document instance to iterate the stories in.
+ * @param   {Function} [cb] Optional: The callback function to call with each story. When this function returns false the loop stops. Passed arguments: `story`, `loopCount`
+ * @return  {Stories|Array} A collection or an array of Story objects.
  *
  * @example
  * stories(doc(), function(story, loopCount){
@@ -3856,27 +3861,27 @@ pub.placeholder = function (textFrame) {
  */
 pub.stories = function(doc, cb) {
 
-  checkNull(doc);
-
-  if(arguments.length === 1 && doc instanceof Document) {
+  if(doc instanceof Document) {
+    if(cb instanceof Function) {
+      return forEach(doc.stories, cb);
+    }
     return doc.stories;
-  } else if (cb instanceof Function) {
-    return forEach(doc.stories, cb);
   }
-  error("stories(), incorrect call. Wrong parameters!");
-  return null;
+
+  error("stories(), invalid container. Use: Document.");
 };
 
 /**
- * @description If no callback function is given it returns a Collection of words in the container otherwise calls the given callback function with each word of the given document, page, story, textFrame, paragraph or line.
+ * @description Returns a collection of all word objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph or Line.
+ * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  words
  *
- * @param   {Document|Page|Story|TextFrame|Paragraph|Line} container The document, page, story, textFrame, paragraph or line instance to iterate the words in.
- * @param   {Function} [cb] The callback function to call with each word. When this function returns false the loop stops. Passed arguments: `word`, `loopCount`.
- * @return  {Words} A collection of Word objects.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line} container The document, page, layer, group, story, textFrame, paragraph or line instance to iterate the words in.
+ * @param   {Function} [cb] Optional: The callback function to call with each word. When this function returns false the loop stops. Passed arguments: `word`, `loopCount`
+ * @return  {Words|Array} A collection or an array of Word objects.
  */
 pub.words = function(container, cb) {
 
@@ -3889,22 +3894,6 @@ pub.words = function(container, cb) {
 // Document Private
 // ----------------------------------------
 
-var forEachTextCollection = function(container, collection, cb) {
-  // var collection;
-  if(container instanceof Document) {
-    collection = container.stories.everyItem()[collection];
-  } else {
-    collection = container.textFrames.everyItem()[collection];
-  }
-
-  for (var i = 0; i < collection.length; i++) {
-    if(cb(collection[i], i) === false) {
-      return false;
-    }
-  }
-  return true;
-};
-
 var getPage = function(page, parentFunctionName) {
   // get a page by number, name, page object or page item, without jumping to the page
   if(isNumber(page)) {
@@ -4003,26 +3992,31 @@ var textCollection = function(collection, legalContainers, container, cb) {
 
   checkNull(container);
 
-  if(!(container.hasOwnProperty("contents") || container instanceof Document || container instanceof Page)) {
+  if(!(container.hasOwnProperty("contents") ||
+       container instanceof Document ||
+       container instanceof Page ||
+       container instanceof Layer ||
+       container instanceof Group)) {
     error(collection + "(), wrong object type. Use: " + legalContainers);
   }
 
-  if(cb instanceof Function) {
-    // callback function is passed
-    if (container instanceof Document || container instanceof Page) {
-      return forEachTextCollection(container, collection, cb);
-    }
-    return forEach(container[collection], cb);
-
-  }
-    // no callback function is passed
+  // collection
   if(container instanceof Document) {
-    return container.stories.everyItem()[collection];
-  } else if (container instanceof Page) {
-    return container.textFrames.everyItem()[collection];
+    collection = container.stories.everyItem()[collection].everyItem().getElements();
+  } else if(container instanceof Page ||
+            container instanceof Layer ||
+            container instanceof Group) {
+    collection = container.textFrames.everyItem()[collection].everyItem().getElements();
+  } else {
+    collection = container[collection];
   }
-  return container[collection];
 
+  if(cb instanceof Function) {
+    // callback function is passed
+    return forEach(collection, cb);
+  } else {
+    return collection;
+  }
 
 };
 
diff --git a/src/includes/data.js b/src/includes/data.js
index a074b86a..5e197379 100644
--- a/src/includes/data.js
+++ b/src/includes/data.js
@@ -25,10 +25,13 @@ forEach = function(collection, cb) {
     }
 
     if(cb(collection[i], i) === false) {
-      return false;
+      if(collection.hasOwnProperty("everyItem")) {
+        return collection.everyItem().getElements().slice(0, i);
+      }
+      return collection.slice(0, i);
     }
   }
-  return true;
+  return collection;
 };
 
 // ----------------------------------------
diff --git a/src/includes/document.js b/src/includes/document.js
index 51514110..4f5373b4 100644
--- a/src/includes/document.js
+++ b/src/includes/document.js
@@ -964,31 +964,28 @@ pub.group = function (pItems, name) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of items otherwise calls the given callback function for each of the PageItems in the given Document, Page, Layer or Group.
+ * @description Returns a collection of all page items in the given container. The container object can be a Document, Page, Layer, Group, Story, Page Item or Text Object.
+ * If a callback function is given, `items()` calls this callback function on each page item of the given container. When the callback function returns false, the loop stops and the `items()` function returns an array of all page items that did not return false.
  *
  * @cat     Document
  * @subcat  Page Items
  * @method  items
  *
- * @param   {Document|Page|Layer|Group} container The container where the PageItems sit in
- * @param   {Function|Boolean} [cb] Optional: The callback function to call for each PageItem. When this function returns false the loop stops. Passed arguments: `item`, `loopCount`.
- * @return  {PageItems} A collection of PageItem objects.
+ * @param   {Document|Page|Layer|Group|Story|PageItem|TextObject} container The document, page, layer, group, story, page item or text object instance to iterate the page items in.
+ * @param   {Function} [cb] Optional: The callback function to call with each page item. When this function returns false the loop stops. Passed arguments: `item`, `loopCount`
+ * @return  {PageItems|Array} A collection or an array of page items.
  */
 pub.items = function(container, cb) {
 
-  if (container instanceof Document
-    || container instanceof Page
-    || container instanceof Layer
-    || container instanceof Group) {
+  if (arguments.length && container.hasOwnProperty("allPageItems")) {
 
-    if(arguments.length === 1 || cb === false) {
-      return container.allPageItems;
-    } else if(cb instanceof Function) {
+    if(cb instanceof Function) {
       return forEach(container.allPageItems, cb);
     }
+    return container.allPageItems;
   }
-  error("items(), Not a valid PageItem container, should be Document, Page, Layer or Group");
-  return null;
+
+  error("items(), Not a valid PageItem container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
 };
 
 /**
@@ -1246,15 +1243,16 @@ pub.addToStory = function(story, itemOrString, insertionPointorMode) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of characters in the container otherwise calls the given callback function with each character of the given document, page, story, textFrame, paragraph, line or word.
+ * @description Returns a collection of all character objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph, Line or Word.
+ * If a callback function is given, `characters()` calls this callback function on each character object of the given container. When the callback function returns false, the loop stops and the `characters()` function returns an array of all characters that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  characters
  *
- * @param   {Document|Page|Story|TextFrame|Paragraph|Line|Word} container The document, page, story, textFrame, paragraph, line or word instance to  iterate the characters in.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame, paragraph, line or word instance to  iterate the characters in.
  * @param   {Function} [cb] Optional: The callback function to call with each character. When this function returns false the loop stops. Passed arguments: `character`, `loopCount`
- * @return  {Characters} A collection of Character objects.
+ * @return  {Characters|Array} A collection or an array of Character objects.
  */
 pub.characters = function(container, cb) {
 
@@ -1264,16 +1262,18 @@ pub.characters = function(container, cb) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of lines in the container otherwise calls the given callback function with each line of the given document, page, story, textFrame or paragraph.
+ * @description Returns a collection of all line objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame or Paragraph. Please note that `lines()` refers to lines of text in a text frame. If you need to construct a geometric line on a page, use `line()` instead.
+ * If a callback function is given, `lines()` calls this callback function on each line object of the given container. When the callback function returns false, the loop stops and the `lines()` function returns an array of all lines that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  lines
  *
- * @param   {Document|Page|Story|TextFrame|Paragraph} container The document, page, story, textFrame or paragraph instance to iterate the lines in.
- * @param   {Function} [cb] Optional: The callback function to call with each line. When this function returns false the loop stops. Passed arguments: `line`, `loopCount`.
- * @return  {Lines} A collection of Line objects.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame or paragraph instance to  iterate the lines in.
+ * @param   {Function} [cb] Optional: The callback function to call with each line. When this function returns false the loop stops. Passed arguments: `line`, `loopCount`
+ * @return  {Lines|Array} A collection or an array of Line objects.
  */
+
 pub.lines = function(container, cb) {
 
   var legalContainers = "Document, Story, Page, TextFrame or Paragraph.";
@@ -1300,15 +1300,16 @@ pub.linkTextFrames = function (textFrameA, textFrameB) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of paragraphs in the container otherwise calls the given callback function with each paragraph of the given document, page, story or textFrame.
+ * @description Returns a collection of all paragraph objects in the given container. The container object can be a Document, Page, Layer, Group, Story or Text Frame.
+ * If a callback function is given, `paragraphs()` calls this callback function on each paragraph object of the given container. When the callback function returns false, the loop stops and the `paragraphs()` function returns an array of all paragraphs that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  paragraphs
  *
- * @param   {Document|Page|Story|TextFrame} container The document, story, page or textFrame instance to iterate the paragraphs in.
- * @param   {Function} [cb] Optional: The callback function to call with each paragraph. When this function returns false the loop stops. Passed arguments: `para`, `loopCount`.
- * @return  {Paragraphs} A collection of Paragraph objects.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame} container The document, page, layer, group, story or textFrame instance to  iterate the paragraphs in.
+ * @param   {Function} [cb] Optional: The callback function to call with each paragraph. When this function returns false the loop stops. Passed arguments: `paragraph`, `loopCount`
+ * @return  {Paragraphs|Array} A collection or an array of Paragraph objects.
  */
 pub.paragraphs = function(container, cb) {
 
@@ -1339,15 +1340,16 @@ pub.placeholder = function (textFrame) {
 };
 
 /**
- * @description If no callback function is given it returns a Collection of items otherwise calls the given callback function with each story of the given document.
+ * @description Returns a collection of all story objects in the given document.
+ * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  stories
  *
- * @param   {Document} doc The document instance to iterate the stories in
- * @param   {Function} [cb] The callback function to call with each story. When this function returns `false` the loop stops. Passed arguments: `story`, `loopCount`.
- * @return  {Stories} A collection of Story objects.
+ * @param   {Document} doc The document instance to iterate the stories in.
+ * @param   {Function} [cb] Optional: The callback function to call with each story. When this function returns false the loop stops. Passed arguments: `story`, `loopCount`
+ * @return  {Stories|Array} A collection or an array of Story objects.
  *
  * @example
  * stories(doc(), function(story, loopCount){
@@ -1357,27 +1359,27 @@ pub.placeholder = function (textFrame) {
  */
 pub.stories = function(doc, cb) {
 
-  checkNull(doc);
-
-  if(arguments.length === 1 && doc instanceof Document) {
+  if(doc instanceof Document) {
+    if(cb instanceof Function) {
+      return forEach(doc.stories, cb);
+    }
     return doc.stories;
-  } else if (cb instanceof Function) {
-    return forEach(doc.stories, cb);
   }
-  error("stories(), incorrect call. Wrong parameters!");
-  return null;
+
+  error("stories(), invalid container. Use: Document.");
 };
 
 /**
- * @description If no callback function is given it returns a Collection of words in the container otherwise calls the given callback function with each word of the given document, page, story, textFrame, paragraph or line.
+ * @description Returns a collection of all word objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph or Line.
+ * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words that did not return false.
  *
  * @cat     Document
  * @subcat  Text
  * @method  words
  *
- * @param   {Document|Page|Story|TextFrame|Paragraph|Line} container The document, page, story, textFrame, paragraph or line instance to iterate the words in.
- * @param   {Function} [cb] The callback function to call with each word. When this function returns false the loop stops. Passed arguments: `word`, `loopCount`.
- * @return  {Words} A collection of Word objects.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line} container The document, page, layer, group, story, textFrame, paragraph or line instance to iterate the words in.
+ * @param   {Function} [cb] Optional: The callback function to call with each word. When this function returns false the loop stops. Passed arguments: `word`, `loopCount`
+ * @return  {Words|Array} A collection or an array of Word objects.
  */
 pub.words = function(container, cb) {
 
@@ -1390,22 +1392,6 @@ pub.words = function(container, cb) {
 // Document Private
 // ----------------------------------------
 
-var forEachTextCollection = function(container, collection, cb) {
-  // var collection;
-  if(container instanceof Document) {
-    collection = container.stories.everyItem()[collection];
-  } else {
-    collection = container.textFrames.everyItem()[collection];
-  }
-
-  for (var i = 0; i < collection.length; i++) {
-    if(cb(collection[i], i) === false) {
-      return false;
-    }
-  }
-  return true;
-};
-
 var getPage = function(page, parentFunctionName) {
   // get a page by number, name, page object or page item, without jumping to the page
   if(isNumber(page)) {
@@ -1504,25 +1490,30 @@ var textCollection = function(collection, legalContainers, container, cb) {
 
   checkNull(container);
 
-  if(!(container.hasOwnProperty("contents") || container instanceof Document || container instanceof Page)) {
+  if(!(container.hasOwnProperty("contents") ||
+       container instanceof Document ||
+       container instanceof Page ||
+       container instanceof Layer ||
+       container instanceof Group)) {
     error(collection + "(), wrong object type. Use: " + legalContainers);
   }
 
-  if(cb instanceof Function) {
-    // callback function is passed
-    if (container instanceof Document || container instanceof Page) {
-      return forEachTextCollection(container, collection, cb);
-    }
-    return forEach(container[collection], cb);
-
-  }
-    // no callback function is passed
+  // collection
   if(container instanceof Document) {
-    return container.stories.everyItem()[collection];
-  } else if (container instanceof Page) {
-    return container.textFrames.everyItem()[collection];
+    collection = container.stories.everyItem()[collection].everyItem().getElements();
+  } else if(container instanceof Page ||
+            container instanceof Layer ||
+            container instanceof Group) {
+    collection = container.textFrames.everyItem()[collection].everyItem().getElements();
+  } else {
+    collection = container[collection];
   }
-  return container[collection];
 
+  if(cb instanceof Function) {
+    // callback function is passed
+    return forEach(collection, cb);
+  } else {
+    return collection;
+  }
 
 };

From 6af6384faf26f1647deedc4af5405ecc3278e561 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Mon, 15 Oct 2018 21:49:41 +0200
Subject: [PATCH 2/8] Improving docs

---
 basil.js                 | 18 ++++++++++--------
 src/includes/data.js     |  6 ++++--
 src/includes/document.js | 12 ++++++------
 3 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/basil.js b/basil.js
index 4d11efbf..8645c772 100644
--- a/basil.js
+++ b/basil.js
@@ -1506,14 +1506,16 @@ pub.swatch = function(){
 // ----------------------------------------
 
 /**
- * @description Used to run a function on all elements of an array. Please note the existence of the convenience methods `stories()`, `paragraphs()`, `lines()`, `words()` and `characters()` that are used to iterate through all instances of the given type in the given document.
+ * @description Used to run a function on all elements of an array. `forEach()` calls this callback function on each element of the given array. When the callback function returns false, the loop stops and an array of all elements up to this point is returned.
+ * Please note the existence of the convenience methods `stories()`, `paragraphs()`, `lines()`, `words()` and `characters()` that are used to iterate through all instances of the given type in the given document.
  *
  * @cat     Data
  * @subcat  Collections
  * @method  forEach
  *
  * @param   {Array} collection The array to be processed.
- * @param   {Function} cb The function that will be called on each element. The call will be like function(item,i) where i is the current index of the item within the array.
+ * @param   {Function} cb The function that will be called on each element. The call will be like `function(item, i)` where `i` is the current index of the item within the array.
+ * @return  {Array} An array of the input array elements.
  */
 forEach = function(collection, cb) {
   for (var i = 0, len = collection.length; i < len; i++) {
@@ -3467,7 +3469,7 @@ pub.group = function (pItems, name) {
 
 /**
  * @description Returns a collection of all page items in the given container. The container object can be a Document, Page, Layer, Group, Story, Page Item or Text Object.
- * If a callback function is given, `items()` calls this callback function on each page item of the given container. When the callback function returns false, the loop stops and the `items()` function returns an array of all page items that did not return false.
+ * If a callback function is given, `items()` calls this callback function on each page item of the given container. When the callback function returns false, the loop stops and the `items()` function returns an array of all page items up to this point.
  *
  * @cat     Document
  * @subcat  Page Items
@@ -3746,7 +3748,7 @@ pub.addToStory = function(story, itemOrString, insertionPointorMode) {
 
 /**
  * @description Returns a collection of all character objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph, Line or Word.
- * If a callback function is given, `characters()` calls this callback function on each character object of the given container. When the callback function returns false, the loop stops and the `characters()` function returns an array of all characters that did not return false.
+ * If a callback function is given, `characters()` calls this callback function on each character object of the given container. When the callback function returns false, the loop stops and the `characters()` function returns an array of all characters up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -3765,7 +3767,7 @@ pub.characters = function(container, cb) {
 
 /**
  * @description Returns a collection of all line objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame or Paragraph. Please note that `lines()` refers to lines of text in a text frame. If you need to construct a geometric line on a page, use `line()` instead.
- * If a callback function is given, `lines()` calls this callback function on each line object of the given container. When the callback function returns false, the loop stops and the `lines()` function returns an array of all lines that did not return false.
+ * If a callback function is given, `lines()` calls this callback function on each line object of the given container. When the callback function returns false, the loop stops and the `lines()` function returns an array of all lines up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -3803,7 +3805,7 @@ pub.linkTextFrames = function (textFrameA, textFrameB) {
 
 /**
  * @description Returns a collection of all paragraph objects in the given container. The container object can be a Document, Page, Layer, Group, Story or Text Frame.
- * If a callback function is given, `paragraphs()` calls this callback function on each paragraph object of the given container. When the callback function returns false, the loop stops and the `paragraphs()` function returns an array of all paragraphs that did not return false.
+ * If a callback function is given, `paragraphs()` calls this callback function on each paragraph object of the given container. When the callback function returns false, the loop stops and the `paragraphs()` function returns an array of all paragraphs up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -3843,7 +3845,7 @@ pub.placeholder = function (textFrame) {
 
 /**
  * @description Returns a collection of all story objects in the given document.
- * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories that did not return false.
+ * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -3873,7 +3875,7 @@ pub.stories = function(doc, cb) {
 
 /**
  * @description Returns a collection of all word objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph or Line.
- * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words that did not return false.
+ * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words up to this point.
  *
  * @cat     Document
  * @subcat  Text
diff --git a/src/includes/data.js b/src/includes/data.js
index 5e197379..ff959092 100644
--- a/src/includes/data.js
+++ b/src/includes/data.js
@@ -7,14 +7,16 @@
 // ----------------------------------------
 
 /**
- * @description Used to run a function on all elements of an array. Please note the existence of the convenience methods `stories()`, `paragraphs()`, `lines()`, `words()` and `characters()` that are used to iterate through all instances of the given type in the given document.
+ * @description Used to run a function on all elements of an array. `forEach()` calls this callback function on each element of the given array. When the callback function returns false, the loop stops and an array of all elements up to this point is returned.
+ * Please note the existence of the convenience methods `stories()`, `paragraphs()`, `lines()`, `words()` and `characters()` that are used to iterate through all instances of the given type in the given document.
  *
  * @cat     Data
  * @subcat  Collections
  * @method  forEach
  *
  * @param   {Array} collection The array to be processed.
- * @param   {Function} cb The function that will be called on each element. The call will be like function(item,i) where i is the current index of the item within the array.
+ * @param   {Function} cb The function that will be called on each element. The call will be like `function(item, i)` where `i` is the current index of the item within the array.
+ * @return  {Array} An array of the input array elements.
  */
 forEach = function(collection, cb) {
   for (var i = 0, len = collection.length; i < len; i++) {
diff --git a/src/includes/document.js b/src/includes/document.js
index 4f5373b4..6772fc19 100644
--- a/src/includes/document.js
+++ b/src/includes/document.js
@@ -965,7 +965,7 @@ pub.group = function (pItems, name) {
 
 /**
  * @description Returns a collection of all page items in the given container. The container object can be a Document, Page, Layer, Group, Story, Page Item or Text Object.
- * If a callback function is given, `items()` calls this callback function on each page item of the given container. When the callback function returns false, the loop stops and the `items()` function returns an array of all page items that did not return false.
+ * If a callback function is given, `items()` calls this callback function on each page item of the given container. When the callback function returns false, the loop stops and the `items()` function returns an array of all page items up to this point.
  *
  * @cat     Document
  * @subcat  Page Items
@@ -1244,7 +1244,7 @@ pub.addToStory = function(story, itemOrString, insertionPointorMode) {
 
 /**
  * @description Returns a collection of all character objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph, Line or Word.
- * If a callback function is given, `characters()` calls this callback function on each character object of the given container. When the callback function returns false, the loop stops and the `characters()` function returns an array of all characters that did not return false.
+ * If a callback function is given, `characters()` calls this callback function on each character object of the given container. When the callback function returns false, the loop stops and the `characters()` function returns an array of all characters up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -1263,7 +1263,7 @@ pub.characters = function(container, cb) {
 
 /**
  * @description Returns a collection of all line objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame or Paragraph. Please note that `lines()` refers to lines of text in a text frame. If you need to construct a geometric line on a page, use `line()` instead.
- * If a callback function is given, `lines()` calls this callback function on each line object of the given container. When the callback function returns false, the loop stops and the `lines()` function returns an array of all lines that did not return false.
+ * If a callback function is given, `lines()` calls this callback function on each line object of the given container. When the callback function returns false, the loop stops and the `lines()` function returns an array of all lines up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -1301,7 +1301,7 @@ pub.linkTextFrames = function (textFrameA, textFrameB) {
 
 /**
  * @description Returns a collection of all paragraph objects in the given container. The container object can be a Document, Page, Layer, Group, Story or Text Frame.
- * If a callback function is given, `paragraphs()` calls this callback function on each paragraph object of the given container. When the callback function returns false, the loop stops and the `paragraphs()` function returns an array of all paragraphs that did not return false.
+ * If a callback function is given, `paragraphs()` calls this callback function on each paragraph object of the given container. When the callback function returns false, the loop stops and the `paragraphs()` function returns an array of all paragraphs up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -1341,7 +1341,7 @@ pub.placeholder = function (textFrame) {
 
 /**
  * @description Returns a collection of all story objects in the given document.
- * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories that did not return false.
+ * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories up to this point.
  *
  * @cat     Document
  * @subcat  Text
@@ -1371,7 +1371,7 @@ pub.stories = function(doc, cb) {
 
 /**
  * @description Returns a collection of all word objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph or Line.
- * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words that did not return false.
+ * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words up to this point.
  *
  * @cat     Document
  * @subcat  Text

From 0860a9ced9fd57b08c24658a6662893031fce080 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Mon, 15 Oct 2018 22:09:42 +0200
Subject: [PATCH 3/8] Added graphics() to loop over the graphics of a container

---
 basil.js                 | 28 +++++++++++++++++++++++++++-
 src/includes/document.js | 28 +++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/basil.js b/basil.js
index 8645c772..9bfd3b17 100644
--- a/basil.js
+++ b/basil.js
@@ -3431,6 +3431,32 @@ pub.duplicate = function(item) {
 
 };
 
+/**
+ * @description Returns a collection of all graphics in the given container. The container object can be a Document, Page, Layer, Group, Story, Page Item or Text Object. This function can be used to get the graphic within a graphic frame and move it independently of its parent frame.
+ * If a callback function is given, `graphics()` calls this callback function on each graphic of the given container. When the callback function returns false, the loop stops and the `graphics()` function returns an array of all graphics up to this point.
+ *
+ * @cat     Document
+ * @subcat  Page Items
+ * @method  graphics
+ *
+ * @param   {Document|Page|Layer|Group|Story|PageItem|TextObject} container The document, page, layer, group, story, page item or text object to iterate the graphics in.
+ * @param   {Function} [cb] The callback function to call with each graphic. When this function returns false the loop stops. Passed arguments: `graphic`, `loopCount`.
+ * @return  {Array} An array of Graphics.
+ */
+pub.graphics = function(container, cb) {
+
+  if (arguments.length && container.hasOwnProperty("allGraphics")) {
+
+    if(cb instanceof Function) {
+      return forEach(container.allGraphics, cb);
+    }
+    return container.allGraphics;
+  }
+
+  error("graphics(), not a valid Graphics container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
+
+};
+
 /**
  * @description Returns the Group instance and sets it if argument Group is given. Groups items to a new group. Returns the resulting group instance. If a string is given as the only argument, the group by the given name will be returned.
  *
@@ -3489,7 +3515,7 @@ pub.items = function(container, cb) {
     return container.allPageItems;
   }
 
-  error("items(), Not a valid PageItem container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
+  error("items(), not a valid PageItem container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
 };
 
 /**
diff --git a/src/includes/document.js b/src/includes/document.js
index 6772fc19..7dc0ba9a 100644
--- a/src/includes/document.js
+++ b/src/includes/document.js
@@ -927,6 +927,32 @@ pub.duplicate = function(item) {
 
 };
 
+/**
+ * @description Returns a collection of all graphics in the given container. The container object can be a Document, Page, Layer, Group, Story, Page Item or Text Object. This function can be used to get the graphic within a graphic frame and move it independently of its parent frame.
+ * If a callback function is given, `graphics()` calls this callback function on each graphic of the given container. When the callback function returns false, the loop stops and the `graphics()` function returns an array of all graphics up to this point.
+ *
+ * @cat     Document
+ * @subcat  Page Items
+ * @method  graphics
+ *
+ * @param   {Document|Page|Layer|Group|Story|PageItem|TextObject} container The document, page, layer, group, story, page item or text object to iterate the graphics in.
+ * @param   {Function} [cb] The callback function to call with each graphic. When this function returns false the loop stops. Passed arguments: `graphic`, `loopCount`.
+ * @return  {Array} An array of Graphics.
+ */
+pub.graphics = function(container, cb) {
+
+  if (arguments.length && container.hasOwnProperty("allGraphics")) {
+
+    if(cb instanceof Function) {
+      return forEach(container.allGraphics, cb);
+    }
+    return container.allGraphics;
+  }
+
+  error("graphics(), not a valid Graphics container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
+
+};
+
 /**
  * @description Returns the Group instance and sets it if argument Group is given. Groups items to a new group. Returns the resulting group instance. If a string is given as the only argument, the group by the given name will be returned.
  *
@@ -985,7 +1011,7 @@ pub.items = function(container, cb) {
     return container.allPageItems;
   }
 
-  error("items(), Not a valid PageItem container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
+  error("items(), not a valid PageItem container, should be Document, Page, Layer, Group, Story, PageItem or Text Object.");
 };
 
 /**

From c62b58b8c2f4d649b084b1ccf6de4d0112a03d32 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Mon, 15 Oct 2018 22:33:13 +0200
Subject: [PATCH 4/8] Adding textStyleRanges() to enable looping over
 textStyleRanges

---
 basil.js                 | 29 ++++++++++++++++++++++++-----
 src/includes/document.js | 29 ++++++++++++++++++++++++-----
 2 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/basil.js b/basil.js
index 9bfd3b17..e913b948 100644
--- a/basil.js
+++ b/basil.js
@@ -3786,7 +3786,7 @@ pub.addToStory = function(story, itemOrString, insertionPointorMode) {
  */
 pub.characters = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page, TextFrame, Paragraph, Line or Word.";
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame, Paragraph, Line or Word.";
   return textCollection("characters", legalContainers, container, cb);
 
 };
@@ -3799,14 +3799,14 @@ pub.characters = function(container, cb) {
  * @subcat  Text
  * @method  lines
  *
- * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame or paragraph instance to  iterate the lines in.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line} container The document, page, layer, group, story, textFrame or paragraph instance to  iterate the lines in.
  * @param   {Function} [cb] Optional: The callback function to call with each line. When this function returns false the loop stops. Passed arguments: `line`, `loopCount`
  * @return  {Lines|Array} A collection or an array of Line objects.
  */
 
 pub.lines = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page, TextFrame or Paragraph.";
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame or Paragraph.";
   return textCollection("lines", legalContainers, container, cb);
 
 };
@@ -3843,7 +3843,7 @@ pub.linkTextFrames = function (textFrameA, textFrameB) {
  */
 pub.paragraphs = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page or TextFrame.";
+  var legalContainers = "Document, Page, Layer, Group, Story or TextFrame.";
   return textCollection("paragraphs", legalContainers, container, cb);
 
 };
@@ -3899,6 +3899,25 @@ pub.stories = function(doc, cb) {
   error("stories(), invalid container. Use: Document.");
 };
 
+/**
+ * @description Returns a collection of all text style range objects in the given container. A text style range is a continuous range of identically formatted text (i.e., three consecutive red words in an otherwise black text of the same style would form a text style range). The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph, Line or Word.
+ * If a callback function is given, `textStyleRanges()` calls this callback function on each text style range object of the given container. When the callback function returns false, the loop stops and the `textStyleRanges()` function returns an array of all text style ranges up to this point.
+ *
+ * @cat     Document
+ * @subcat  Text
+ * @method  textStyleRanges
+ *
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame, paragraph, line or word instance to iterate the text style ranges in.
+ * @param   {Function} [cb] Optional: The callback function to call with each text style range. When this function returns false the loop stops. Passed arguments: `textStyleRange`, `loopCount`
+ * @return  {TextStyleRanges|Array} A collection or an array of TextStyleRange objects.
+ */
+pub.textStyleRanges = function(container, cb) {
+
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame, Paragraph, Line or Word.";
+  return textCollection("textStyleRanges", legalContainers, container, cb);
+
+};
+
 /**
  * @description Returns a collection of all word objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph or Line.
  * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words up to this point.
@@ -3913,7 +3932,7 @@ pub.stories = function(doc, cb) {
  */
 pub.words = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page, TextFrame, Paragraph or Line.";
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame, Paragraph or Line.";
   return textCollection("words", legalContainers, container, cb);
 
 };
diff --git a/src/includes/document.js b/src/includes/document.js
index 7dc0ba9a..34788dbc 100644
--- a/src/includes/document.js
+++ b/src/includes/document.js
@@ -1282,7 +1282,7 @@ pub.addToStory = function(story, itemOrString, insertionPointorMode) {
  */
 pub.characters = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page, TextFrame, Paragraph, Line or Word.";
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame, Paragraph, Line or Word.";
   return textCollection("characters", legalContainers, container, cb);
 
 };
@@ -1295,14 +1295,14 @@ pub.characters = function(container, cb) {
  * @subcat  Text
  * @method  lines
  *
- * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame or paragraph instance to  iterate the lines in.
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line} container The document, page, layer, group, story, textFrame or paragraph instance to  iterate the lines in.
  * @param   {Function} [cb] Optional: The callback function to call with each line. When this function returns false the loop stops. Passed arguments: `line`, `loopCount`
  * @return  {Lines|Array} A collection or an array of Line objects.
  */
 
 pub.lines = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page, TextFrame or Paragraph.";
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame or Paragraph.";
   return textCollection("lines", legalContainers, container, cb);
 
 };
@@ -1339,7 +1339,7 @@ pub.linkTextFrames = function (textFrameA, textFrameB) {
  */
 pub.paragraphs = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page or TextFrame.";
+  var legalContainers = "Document, Page, Layer, Group, Story or TextFrame.";
   return textCollection("paragraphs", legalContainers, container, cb);
 
 };
@@ -1395,6 +1395,25 @@ pub.stories = function(doc, cb) {
   error("stories(), invalid container. Use: Document.");
 };
 
+/**
+ * @description Returns a collection of all text style range objects in the given container. A text style range is a continuous range of identically formatted text (i.e., three consecutive red words in an otherwise black text of the same style would form a text style range). The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph, Line or Word.
+ * If a callback function is given, `textStyleRanges()` calls this callback function on each text style range object of the given container. When the callback function returns false, the loop stops and the `textStyleRanges()` function returns an array of all text style ranges up to this point.
+ *
+ * @cat     Document
+ * @subcat  Text
+ * @method  textStyleRanges
+ *
+ * @param   {Document|Page|Layer|Group|Story|TextFrame|Paragraph|Line|Word} container The document, page, layer, group, story, textFrame, paragraph, line or word instance to iterate the text style ranges in.
+ * @param   {Function} [cb] Optional: The callback function to call with each text style range. When this function returns false the loop stops. Passed arguments: `textStyleRange`, `loopCount`
+ * @return  {TextStyleRanges|Array} A collection or an array of TextStyleRange objects.
+ */
+pub.textStyleRanges = function(container, cb) {
+
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame, Paragraph, Line or Word.";
+  return textCollection("textStyleRanges", legalContainers, container, cb);
+
+};
+
 /**
  * @description Returns a collection of all word objects in the given container. The container object can be a Document, Page, Layer, Group, Story, Text Frame, Paragraph or Line.
  * If a callback function is given, `words()` calls this callback function on each word object of the given container. When the callback function returns false, the loop stops and the `words()` function returns an array of all words up to this point.
@@ -1409,7 +1428,7 @@ pub.stories = function(doc, cb) {
  */
 pub.words = function(container, cb) {
 
-  var legalContainers = "Document, Story, Page, TextFrame, Paragraph or Line.";
+  var legalContainers = "Document, Page, Layer, Group, Story, TextFrame, Paragraph or Line.";
   return textCollection("words", legalContainers, container, cb);
 
 };

From 6fd4e61c5612829062ef954c804a8ccc48c7d352 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Mon, 15 Oct 2018 23:02:54 +0200
Subject: [PATCH 5/8] Updating changelog.txt

---
 changelog.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/changelog.txt b/changelog.txt
index 99067507..07c4364d 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -30,6 +30,8 @@ basil.js x.x.x DAY MONTH YEAR
   allows to arrange page items or layers in z-space
 + Added property()
   sets a property of an object to a given value
++ Added graphics() to loop over graphics in a given container
++ Added textStyleRanges() to loop over text style ranges in a given container
 + Added swatch()
   returns a color or gradient of a given name
 + Added revert()

From b63267e415370798218c85768756bb5499f15945 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Mon, 15 Oct 2018 23:57:11 +0200
Subject: [PATCH 6/8] Allowed text frames and text objects as containers for
 stories()

---
 basil.js                 | 24 +++++++++++++++---------
 src/includes/document.js | 24 +++++++++++++++---------
 2 files changed, 30 insertions(+), 18 deletions(-)

diff --git a/basil.js b/basil.js
index e913b948..54078f0f 100644
--- a/basil.js
+++ b/basil.js
@@ -3870,33 +3870,39 @@ pub.placeholder = function (textFrame) {
 };
 
 /**
- * @description Returns a collection of all story objects in the given document.
- * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories up to this point.
+ * @description Returns a collection of all story objects in the given document or returns the parent story of a certain element. These elements can be text frames or text objects.
+ * If a callback function is given, `stories()` calls this callback function on each story object of the given document or on the parent story of the given element. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories up to this point.
  *
  * @cat     Document
  * @subcat  Text
  * @method  stories
  *
- * @param   {Document} doc The document instance to iterate the stories in.
+ * @param   {Document} container The document instance to iterate the stories in or the element whose parent story to get.
  * @param   {Function} [cb] Optional: The callback function to call with each story. When this function returns false the loop stops. Passed arguments: `story`, `loopCount`
  * @return  {Stories|Array} A collection or an array of Story objects.
  *
  * @example
- * stories(doc(), function(story, loopCount){
+ * stories(container(), function(story, loopCount){
  *   println("Number of words in each Story:");
  *   println(story.words.length);
  * });
  */
-pub.stories = function(doc, cb) {
+pub.stories = function(container, cb) {
 
-  if(doc instanceof Document) {
+  if(container instanceof Document) {
+    if(cb instanceof Function) {
+      return forEach(container.stories, cb);
+    }
+    return container.stories;
+  } else if(container.hasOwnProperty("parentStory")) {
+    var parentStoryArray = [container.parentStory];
     if(cb instanceof Function) {
-      return forEach(doc.stories, cb);
+      return forEach(parentStoryArray, cb);
     }
-    return doc.stories;
+    return parentStoryArray;
   }
 
-  error("stories(), invalid container. Use: Document.");
+  error("stories(), invalid container. Use: Document, Text Frame or Text Object.");
 };
 
 /**
diff --git a/src/includes/document.js b/src/includes/document.js
index 34788dbc..4cfdce56 100644
--- a/src/includes/document.js
+++ b/src/includes/document.js
@@ -1366,33 +1366,39 @@ pub.placeholder = function (textFrame) {
 };
 
 /**
- * @description Returns a collection of all story objects in the given document.
- * If a callback function is given, `stories()` calls this callback function on each story object of the given document. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories up to this point.
+ * @description Returns a collection of all story objects in the given document or returns the parent story of a certain element. These elements can be text frames or text objects.
+ * If a callback function is given, `stories()` calls this callback function on each story object of the given document or on the parent story of the given element. When the callback function returns false, the loop stops and the `stories()` function returns an array of all stories up to this point.
  *
  * @cat     Document
  * @subcat  Text
  * @method  stories
  *
- * @param   {Document} doc The document instance to iterate the stories in.
+ * @param   {Document} container The document instance to iterate the stories in or the element whose parent story to get.
  * @param   {Function} [cb] Optional: The callback function to call with each story. When this function returns false the loop stops. Passed arguments: `story`, `loopCount`
  * @return  {Stories|Array} A collection or an array of Story objects.
  *
  * @example
- * stories(doc(), function(story, loopCount){
+ * stories(container(), function(story, loopCount){
  *   println("Number of words in each Story:");
  *   println(story.words.length);
  * });
  */
-pub.stories = function(doc, cb) {
+pub.stories = function(container, cb) {
 
-  if(doc instanceof Document) {
+  if(container instanceof Document) {
+    if(cb instanceof Function) {
+      return forEach(container.stories, cb);
+    }
+    return container.stories;
+  } else if(container.hasOwnProperty("parentStory")) {
+    var parentStoryArray = [container.parentStory];
     if(cb instanceof Function) {
-      return forEach(doc.stories, cb);
+      return forEach(parentStoryArray, cb);
     }
-    return doc.stories;
+    return parentStoryArray;
   }
 
-  error("stories(), invalid container. Use: Document.");
+  error("stories(), invalid container. Use: Document, Text Frame or Text Object.");
 };
 
 /**

From 77ff9aaa12b07db6822fa67b2968a4fcb1594089 Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Wed, 17 Oct 2018 00:04:44 +0200
Subject: [PATCH 7/8] Added document-tests.jsx

---
 test/document-tests.jsx | 225 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 225 insertions(+)
 create mode 100644 test/document-tests.jsx

diff --git a/test/document-tests.jsx b/test/document-tests.jsx
new file mode 100644
index 00000000..7adb651b
--- /dev/null
+++ b/test/document-tests.jsx
@@ -0,0 +1,225 @@
+/* globals assert */
+if (!$.global.VERSION) {
+  var basilTest = null;
+  // @include "../basil.js";
+}
+if (!basilTest) {
+  // @include "../lib/basil.test.js";
+}
+
+basilTest("EnvironmentTests", {
+
+  setUpTest: function() {
+  },
+
+  tearDownTest: function() {
+  },
+
+  setUp: function() {
+  },
+
+  tearDown: function() {
+    close(SaveOptions.no);
+  },
+
+  testGraphics: function() {
+    var myDoc = doc();
+
+    var basilFolder = File($.fileName).parent.parent;
+    var basilImg = File(basilFolder + "/lib/basil.png");
+    var img = image(basilImg, 0, 0);
+    var img2 = image(basilImg, 10, 10);
+    var img3 = image(basilImg, 20, 20);
+
+    var g = graphics(doc());
+
+    assert(g.constructor.name === "Array");
+    assert(g[0] instanceof Image);
+
+    var gCounter = 0;
+    var gcb1 = graphics(doc(), function(graphic, i) {
+      gCounter++;
+    })
+    assert(gcb1.constructor.name === "Array");
+    assert(gCounter === g.length);
+    assert(gCounter === gcb1.length);
+
+    var gcb2 = graphics(doc(), function(graphic, i) {
+      if(graphic.geometricBounds[0] < 10) {
+        return false;
+      }
+    })
+    assert(gcb2.constructor.name === "Array");
+    assert(gcb2[0] instanceof Image);
+    assert(gcb2.length < gcb1.length);
+  },
+
+  testCharacters: function() {
+    var myDoc = doc();
+    var tf = text(LOREM, 0, 0, 100, 100);
+
+    var c = characters(tf);
+    assert(c instanceof Characters);
+
+    var cCounter = 0;
+    var ccb1 = characters(tf, function(character, i) {
+      cCounter++;
+    })
+    assert(ccb1 instanceof Characters);
+    assert(cCounter === c.length);
+    assert(cCounter === ccb1.length);
+
+    var ccb2 = characters(tf, function(character, i) {
+      if(character.contents === "c") {
+        return false;
+      }
+    })
+    assert(isArray(ccb2));
+    assert(ccb2[0] instanceof Character);
+    assert(ccb2.length < ccb1.length);
+  },
+
+  testLines: function() {
+    var myDoc = doc();
+    units(MM);
+    textSize(12);
+    var tf = text(LOREM, 0, 0, 100, 100);
+
+    var l = lines(tf);
+    assert(l instanceof Lines);
+
+    var lCounter = 0;
+    var lcb1 = lines(tf, function(line, i) {
+      lCounter++;
+    })
+    assert(lcb1 instanceof Lines);
+    assert(lCounter === l.length);
+    assert(lCounter === lcb1.length);
+
+    var lcb2 = lines(tf, function(line, i) {
+      if(line.baseline > 10) {
+        return false;
+      }
+    })
+    assert(isArray(lcb2));
+    assert(lcb2[0] instanceof Line);
+    assert(lcb2.length < lcb1.length);
+  },
+
+  testParagraphs: function() {
+    var myDoc = doc();
+    var tf = text(LOREM + "\r" + LOREM + "\r" + LOREM, 0, 0, 100, 100);
+
+    var p = paragraphs(tf);
+    assert(p instanceof Paragraphs);
+    assert(p.length === 3);
+
+    var pCounter = 0;
+    var pcb1 = paragraphs(tf, function(paragraph, i) {
+      pCounter++;
+    })
+    assert(pcb1 instanceof Paragraphs);
+    assert(pCounter === p.length);
+    assert(pCounter === pcb1.length);
+
+    var pcb2 = paragraphs(tf, function(paragraph, i) {
+      if(paragraph.lines[0].baseline > 10) {
+        return false;
+      }
+    })
+    assert(isArray(pcb2));
+    assert(pcb2[0] instanceof Paragraph);
+    assert(pcb2.length < pcb1.length);
+  },
+
+  testStories: function() {
+    var myDoc = doc();
+    textSize(12);
+    var tf1 = text(LOREM, 0, 0, 100, 100);
+    textSize(14);
+    var tf2 = text(LOREM, 0, 0, 100, 100);
+
+    var s = stories(doc());
+    assert(s instanceof Stories);
+    assert(s.length === 2);
+
+    var sCounter = 0;
+    var scb1 = stories(doc(), function(story, i) {
+      sCounter++;
+    })
+    assert(scb1 instanceof Stories);
+    assert(sCounter === s.length);
+    assert(sCounter === scb1.length);
+
+    var scb2 = stories(doc(), function(story, i) {
+      if(story.pointSize === 14) {
+        return false;
+      }
+    })
+    assert(isArray(scb2));
+    assert(scb2[0] instanceof Story);
+    assert(scb2.length < scb1.length);
+
+    linkTextFrames(tf1, tf2);
+    var s = stories(doc());
+    assert(s.length === 1);
+  },
+
+  testTextStyleRanges: function() {
+    var myDoc = doc();
+    textFont("Helvetica");
+    var tf = text(LOREM + " " + LOREM, 0, 0, 100, 100);
+    typo(tf.lines[2], "fillColor", color(255, 0, 0));
+    typo(tf.lines[2].words[2], "fontStyle", "Italic");
+
+    var tsr = textStyleRanges(tf);
+    assert(tsr instanceof TextStyleRanges);
+    assert(tsr.length === 5);
+
+    var tsrCounter = 0;
+    var tsrcb1 = textStyleRanges(tf, function(textStyleRange, i) {
+      tsrCounter++;
+    })
+    assert(tsrcb1 instanceof TextStyleRanges);
+    assert(tsrCounter === tsr.length);
+    assert(tsrCounter === tsrcb1.length);
+
+    var tsrcb2 = textStyleRanges(tf, function(textStyleRange, i) {
+      if(textStyleRange.fontStyle === "Italic") {
+        return false;
+      }
+    })
+    assert(isArray(tsrcb2));
+    assert(tsrcb2[0] instanceof TextStyleRange);
+    assert(tsrcb2.length < tsrcb1.length);
+  },
+
+  testWords: function() {
+    var myDoc = doc();
+    var tf = text(LOREM, 0, 0, 100, 100);
+
+    var w = words(tf);
+    assert(w instanceof Words);
+
+    var wCounter = 0;
+    var wcb1 = words(tf, function(word, i) {
+      wCounter++;
+    })
+    assert(wcb1 instanceof Words);
+    assert(wCounter === w.length);
+    assert(wCounter === wcb1.length);
+
+    var wcb2 = words(tf, function(word, i) {
+      if(word.contents === "consectetur") {
+        return false;
+      }
+    })
+    assert(isArray(wcb2));
+    assert(wcb2[0] instanceof Word);
+    assert(wcb2.length < wcb1.length);
+  }
+
+});
+
+// print collected test results
+basilTest.result();

From e2c214bd3e3157fac5d97f0ef8f574e01d37a00b Mon Sep 17 00:00:00 2001
From: trych <timorychert@23x5.net>
Date: Wed, 17 Oct 2018 00:05:39 +0200
Subject: [PATCH 8/8] Added document-tests.jsx to all-tests.jsx

---
 test/all-tests.jsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/all-tests.jsx b/test/all-tests.jsx
index 93da39d6..767fca6b 100644
--- a/test/all-tests.jsx
+++ b/test/all-tests.jsx
@@ -1,6 +1,7 @@
 // @include "color-tests.jsx";
 // @include "conversion-tests.jsx";
 // @include "data-tests.jsx";
+// @include "document-tests.jsx";
 // @include "environment-tests.jsx";
 // @include "group-tests.jsx";
 // @include "input-tests.jsx";