<commit_before>(function( $ ) {

  /*
   * Configuration.
   */
  var key = ''; // TODO add your API key
  var datasource = function (products, callback) {
    // TODO add your data call
    // Example: $.getJSON(your_url, url_params, callback);
    /*
     * @param products A list of product to get data for.
     * @param callback The function to call with the product data.
     *
     * The callback method takes a list of dictionaries/objects containing the
     * data for each product in the products argument. This is used for
     * rendering templates so if your template uses the variables id, name and
     * price and you are the list [2,42,1337] as the products argument then the
     * argument for callback should be like:
     *
     *     [{'id': 2,
     *       'name': 'The name of 2',
     *       'price' 1.99
     *       },
     *      {'id': 42,
     *       'name': 'The name of 42',
     *       'price' 49.0
     *       },
     *      {'id': 1337,
     *       'name': 'The name of 1337',
     *       'price' 100.0}]
     *
     * The order dosn't matter but all dictionaries/object MUST have an entry
     * named 'id' with the products ID. Also if the ID is not used in any
     * template.
     */
  };

  /**************************************************************************/

  /**
   * Main invocation method for The Social Digits plugin.
   *
   * @param api The name of the API.
   * @param args The arguments for the API except 'key' and 'visitor' argument
   *             which are handled automaticly.
   * @param template A jQuery selector for the template to be used.
   */
  $.fn.thesocialdigits = function(api, args, template) {
    var elm = this;

    callAPI(api, args, function(data) {
      if('result' in data) {
        datasource(data.result, function(products) {
          buildHTML(elm, data.result, template, products);
        });
      }
    });
  };

  /*
   * Build HTML from template.
   */
  function buildHTML(elm, ids, template, _products) {
    // ensure the correct sorting of product data
    products = [];

    for (var i in ids) {
      var id = ids[i];

      for(var j in _products) {
        if(_products[j]['id'] == id) {
          products[i] = _products[j];
          break;
        }
      }
    }

    // generate the html from the template
    elm.append(renderTemplate($(template).html(), products));

    // add click logging
    for (var i in ids) {
      var id = ids[i];

      $('a[rel="__tsd-' + id + '"]').click({'id': id}, function(event) {
        var href = $(this).attr('href');
        setTimeout('window.location.href = "' + href + '";', 1000); // always go

        callAPI('__log_click', {'product': event.data.id}, function() {
          window.location.href = href; // go on success
        });

        return false; // don't go yet
      });
    }
  }

  /*
   * Primitive template rendering.
   */
  function renderTemplate(template, products) {
    var output = '';

    for (var i in products) {
      var product = products[i];
      var tmp = template.replace(new RegExp('<a', 'g'), '<a rel="__tsd-' + product['id'] + '"');

      for (var key in product) {
        tmp = tmp.replace(new RegExp('{' + key + '}', 'g'), product[key]);
      }

      output += tmp;
    }

    return output;
  }

  /*
   * Base function to call the API.
   */
  function callAPI(api, args, callback) {
    args.key = key;
    var url = 'http://api.thesocialdigits.com/v1/' + api + '?callback=?';
    var data = {
            'payload': JSON.stringify(args)
          };

    $.getJSON(url, data, callback);
  }

})( jQuery );
<commit_msg>Small debugging to make IE happy.<commit_after>(function( $ ) {

  /*
   * Configuration.
   */
  var key = ''; // TODO add your API key
  var datasource = function (products, callback) {
    // TODO add your data call
    // Example: $.getJSON(your_url, url_params, callback);
    /*
     * @param products A list of product to get data for.
     * @param callback The function to call with the product data.
     *
     * The callback method takes a list of dictionaries/objects containing the
     * data for each product in the products argument. This is used for
     * rendering templates so if your template uses the variables id, name and
     * price and you are the list [2,42,1337] as the products argument then the
     * argument for callback should be like:
     *
     *     [{'id': 2,
     *       'name': 'The name of 2',
     *       'price' 1.99
     *       },
     *      {'id': 42,
     *       'name': 'The name of 42',
     *       'price' 49.0
     *       },
     *      {'id': 1337,
     *       'name': 'The name of 1337',
     *       'price' 100.0}]
     *
     * The order dosn't matter but all dictionaries/object MUST have an entry
     * named 'id' with the products ID. Also if the ID is not used in any
     * template.
     */
  };

  /**************************************************************************/

  /**
   * Main invocation method for The Social Digits plugin.
   *
   * @param api The name of the API.
   * @param args The arguments for the API except 'key' and 'visitor' argument
   *             which are handled automaticly.
   * @param template A jQuery selector for the template to be used.
   */
  $.fn.thesocialdigits = function(api, args, template) {
    var elm = this;

    callAPI(api, args, function(data) {
      if('result' in data) {
        datasource(data.result, function(products) {
          buildHTML(elm, data.result, template, products);
        });
      }
    });
  };

  /*
   * Build HTML from template.
   */
  function buildHTML(elm, ids, template, _products) {
    // ensure the correct sorting of product data
    var products = [];

    for (var i in ids) {
      var id = ids[i];

      for(var j in _products) {
        if(_products[j]['id'] == id) {
          products[i] = _products[j];
          break;
        }
      }
    }

    // generate the html from the template
    elm.append(renderTemplate($(template).html(), products));

    // add click logging
    for (var i in ids) {
      var id = ids[i];

      $('a[rel="__tsd-' + id + '"]').click({'id': id}, function(event) {
        var href = $(this).attr('href');
        setTimeout('window.location.href = "' + href + '";', 500); // always go

        callAPI('__log_click', {'product': event.data.id}, function() {
          window.location.href = href; // go on success
        });

        return false; // don't go yet
      });
    }
  }

  /*
   * Primitive template rendering.
   */
  function renderTemplate(template, products) {
    var output = '';

    for (var i in products) {
      var product = products[i];
      var tmp = template.replace(new RegExp('<a', 'g'), '<a rel="__tsd-' + product['id'] + '"');

      for (var key in product) {
        tmp = tmp.replace(new RegExp('{' + key + '}', 'g'), product[key]);
      }

      output += tmp;
    }

    return output;
  }

  /*
   * Base function to call the API.
   */
  function callAPI(api, args, callback) {
    args.key = key;
    var url = 'http://api.thesocialdigits.com/v1/' + api + '?callback=?';
    var data = {
            'payload': JSON.stringify(args)
          };

    $.getJSON(url, data, callback);
  }

})( jQuery );
<|endoftext|>


<commit_before>function (doc) {
    var media = 0, attachments = {}, value;

    if (doc.doc_type === "XFormInstance") {
        if (doc.xmlns) {
            for (var key in doc._attachments) {
                if (doc._attachments.hasOwnProperty(key) &&
                    doc._attachments[key].content_type !== "text/xml") {
                    media += 1;
                    attachments[key] = doc._attachments[key];
                }
            }
            if (media > 0) {
                value = {
                    xmlns: doc.xmlns,
                    attachments: attachments,
                    date: doc.received_on
                };
                emit([doc.domain, doc.app_id, doc.xmlns, doc.received_on], value);
            }
        }
    }
}
<commit_msg>Update couch attachments view for external_blobs<commit_after>function (doc) {
    var media = 0, attachments = {}, value;

    if (doc.doc_type === "XFormInstance") {
        if (doc.xmlns) {
            for (var key in doc._attachments) {
                if (doc._attachments.hasOwnProperty(key) &&
                    doc._attachments[key].content_type !== "text/xml") {
                    media += 1;
                    attachments[key] = doc._attachments[key];
                }
            }
            for (var key in doc.external_blobs) {
                if (doc.external_blobs.hasOwnProperty(key) &&
                    doc.external_blobs[key].content_type !== "text/xml") {
                    media += 1;
                    attachments[key] = doc.external_blobs[key];
                }
            }
            if (media > 0) {
                value = {
                    xmlns: doc.xmlns,
                    attachments: attachments,
                    date: doc.received_on
                };
                emit([doc.domain, doc.app_id, doc.xmlns, doc.received_on], value);
            }
        }
    }
}
<|endoftext|>


<commit_before>/**
 * Given a DOM subtree, wraps it in a list, and puts each line into its own
 * list item.
 *
 * @param {Node} node modified in place.  Its content is pulled into an
 *     HTMLOListElement, and each line is moved into a separate list item.
 *     This requires cloning elements, so the input might not have unique
 *     IDs after numbering.
 */
function numberLines(node, opt_startLineNum) {
  var nocode = /(?:^|\s)nocode(?:\s|$)/;
  var lineBreak = /\r\n?|\n/;

  var document = node.ownerDocument;

  var whitespace;
  if (node.currentStyle) {
    whitespace = node.currentStyle.whiteSpace;
  } else if (window.getComputedStyle) {
    whitespace = document.defaultView.getComputedStyle(node, null)
        .getPropertyValue('white-space');
  }
  // If it's preformatted, then we need to split lines on line breaks
  // in addition to
s.
  var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);

  var li = document.createElement('LI');
  while (node.firstChild) {
    li.appendChild(node.firstChild);
  }
  // An array of lines.  We split below, so this is initialized to one
  // un-split line.
  var listItems = [li];

  function walk(node) {
    switch (node.nodeType) {
      case 1:  // Element
        if (nocode.test(node.className)) { console.log('nocode'); break; }
console.log('  el=' + node.nodeName);
        if ('BR' === node.nodeName) {
          breakAfter(node);
          // Discard the
 since it is now flush against a
.
          if (node.parentNode) {
            node.parentNode.removeChild(node);
          }
        } else {
          for (var child = node.firstChild; child; child = child.nextSibling) {
            walk(child);
          }
        }
        break;
      case 3: case 4:  // Text
        if (isPreformatted) {
          var text = node.nodeValue;
          var match = text.match(lineBreak);
          if (match) {
            var firstLine = text.substring(0, match.index);
            node.nodeValue = firstLine;
            var tail = text.substring(match.index + match[0].length);
console.log('split "' + text.replace(/\r\n?|\n/g, '\\n') + '" into "' + firstLine + '" and "' + tail.replace(/\r\n?|\n/g, '\\n') + '"');
            if (tail) {
              var parent = node.parentNode;
              parent.insertBefore(
                  document.createTextNode(tail), node.nextSibling);
            }
            breakAfter(node);
            if (!firstLine) {
              // Don't leave blank text nodes in the DOM.
              node.parentNode.removeChild(node);
            }
          }
        }
        break;
    }
  }

  // Split a line after the given node.
  function breakAfter(lineEndNode) {
    // If there's nothing to the right, then we can skip ending the line
    // here, and move root-wards since splitting just before an end-tag
    // would require us to create a bunch of empty copies.
    while (!lineEndNode.nextSibling) {
      lineEndNode = lineEndNode.parentNode;
      if (!lineEndNode) { return; }
    }

    function breakLeftOf(limit, copy) {
      // Clone shallowly if this node needs to be on both sides of the break.
      var rightSide = copy ? limit.cloneNode(false) : limit;
      var parent = limit.parentNode;
      if (parent) {
        // We clone the parent chain.
        // This helps us resurrect important styling elements that cross lines.
        // E.g. in Foo
Bar
        // should be rewritten to
Foo

Bar
.
        var parentClone = breakLeftOf(parent, 1);
        // Move the clone and everything to the right of the original
        // onto the cloned parent.
        var next = limit.nextSibling;
        parentClone.appendChild(rightSide);
        for (var sibling = next; sibling; sibling = next) {
          next = sibling.nextSibling;
          parentClone.appendChild(sibling);
        }
      }
      return rightSide;
    }

    var split = breakLeftOf(lineEndNode.nextSibling, 0);

    // Walk the parent chain until we reach an unattached LI.
    for (var parent; (parent = split.parentNode);) { split = parent; }
    // Put it on the list of lines for later processing.
    listItems.push(split);
console.log('pushing listItem length=' + listItems.length);
  }

  // Split lines while there are lines left to split.
  for (var i = 0;  // Number of lines that have been split so far.
       i < listItems.length;  // length updated by breakAfter calls.
       ++i) {
console.log('walking list item ' + i + ' : ' + listItems[i].innerHTML.replace(/[\r\n]+/, '\\n'));
    walk(listItems[i]);
  }

  // Make sure numeric indices show correctly.
  if (opt_startLineNum === (opt_startLineNum|0)) {
    listItems[0].setAttribute('value', opt_startLineNum);
  }

  var ol = document.createElement('OL');
  ol.className = 'linenums';
  var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
  for (var i = 0, n = listItems.length; i < n; ++i) {
    li = listItems[i];
    // Stick a class on the LIs so that stylesheets can
    // color odd/even rows, or any other row pattern that
    // is co-prime with 10.
    li.className = 'L' + ((i + offset) % 10);
    if (!li.firstChild) {
      li.appendChild(document.createTextNode('\xA0'));
    }
    ol.appendChild(li);
  }

  node.appendChild(ol);
}<commit_msg>logging for IE<commit_after>/**
 * Given a DOM subtree, wraps it in a list, and puts each line into its own
 * list item.
 *
 * @param {Node} node modified in place.  Its content is pulled into an
 *     HTMLOListElement, and each line is moved into a separate list item.
 *     This requires cloning elements, so the input might not have unique
 *     IDs after numbering.
 */
function numberLines(node, opt_startLineNum) {
  var nocode = /(?:^|\s)nocode(?:\s|$)/;
  var lineBreak = /\r\n?|\n/;

  var document = node.ownerDocument;

  var whitespace;
  if (node.currentStyle) {
    whitespace = node.currentStyle.whiteSpace;
  } else if (window.getComputedStyle) {
    whitespace = document.defaultView.getComputedStyle(node, null)
        .getPropertyValue('white-space');
  }
  // If it's preformatted, then we need to split lines on line breaks
  // in addition to
s.
  var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3);

  var li = document.createElement('LI');
  while (node.firstChild) {
    li.appendChild(node.firstChild);
  }
  // An array of lines.  We split below, so this is initialized to one
  // un-split line.
  var listItems = [li];

  function walk(node) {
    switch (node.nodeType) {
      case 1:  // Element
        if (nocode.test(node.className)) { console.log('nocode'); break; }
console.log('  el=' + node.nodeName);
        if ('BR' === node.nodeName) {
          breakAfter(node);
          // Discard the
 since it is now flush against a
.
          if (node.parentNode) {
            node.parentNode.removeChild(node);
          }
        } else {
          for (var child = node.firstChild; child; child = child.nextSibling) {
            walk(child);
          }
        }
        break;
      case 3: case 4:  // Text
        if (isPreformatted) {
          var text = node.nodeValue;
          var match = text.match(lineBreak);
          if (match) {
            var firstLine = text.substring(0, match.index);
            node.nodeValue = firstLine;
            var tail = text.substring(match.index + match[0].length);
console.log('split "' + text.replace(/\r\n?|\n/g, '\\n') + '" into "' + firstLine + '" and "' + tail.replace(/\r\n?|\n/g, '\\n') + '"');
            if (tail) {
              var parent = node.parentNode;
              parent.insertBefore(
                  document.createTextNode(tail), node.nextSibling);
            }
            breakAfter(node);
            if (!firstLine) {
              // Don't leave blank text nodes in the DOM.
              node.parentNode.removeChild(node);
            }
          }
        }
        break;
    }
  }

  // Split a line after the given node.
  function breakAfter(lineEndNode) {
    // If there's nothing to the right, then we can skip ending the line
    // here, and move root-wards since splitting just before an end-tag
    // would require us to create a bunch of empty copies.
    while (!lineEndNode.nextSibling) {
      lineEndNode = lineEndNode.parentNode;
      if (!lineEndNode) { return; }
    }

    function breakLeftOf(limit, copy) {
      // Clone shallowly if this node needs to be on both sides of the break.
      var rightSide = copy ? limit.cloneNode(false) : limit;
      var parent = limit.parentNode;
      if (parent) {
        // We clone the parent chain.
        // This helps us resurrect important styling elements that cross lines.
        // E.g. in Foo
Bar
        // should be rewritten to
Foo

Bar
.
        var parentClone = breakLeftOf(parent, 1);
        // Move the clone and everything to the right of the original
        // onto the cloned parent.
        var next = limit.nextSibling;
        parentClone.appendChild(rightSide);
        for (var sibling = next; sibling; sibling = next) {
          next = sibling.nextSibling;
          parentClone.appendChild(sibling);
        }
      }
      return rightSide;
    }

    var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);

    // Walk the parent chain until we reach an unattached LI.
    for (var parent; (parent = copiedListItem.parentNode);) { copiedListItem = parent; }
    // Put it on the list of lines for later processing.
    listItems.push(copiedListItem);
console.log('pushing listItem length=' + listItems.length + ', copy=' + copiedListItem.nodeName);
  }

  // Split lines while there are lines left to split.
  for (var i = 0;  // Number of lines that have been split so far.
       i < listItems.length;  // length updated by breakAfter calls.
       ++i) {
console.log('walking list item ' + i + ' : ' + listItems[i].innerHTML.replace(/[\r\n]+/, '\\n'));
    walk(listItems[i]);
  }

  // Make sure numeric indices show correctly.
  if (opt_startLineNum === (opt_startLineNum|0)) {
    listItems[0].setAttribute('value', opt_startLineNum);
  }

  var ol = document.createElement('OL');
  ol.className = 'linenums';
  var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
  for (var i = 0, n = listItems.length; i < n; ++i) {
    li = listItems[i];
    // Stick a class on the LIs so that stylesheets can
    // color odd/even rows, or any other row pattern that
    // is co-prime with 10.
    li.className = 'L' + ((i + offset) % 10);
    if (!li.firstChild) {
      li.appendChild(document.createTextNode('\xA0'));
    }
    ol.appendChild(li);
  }

  node.appendChild(ol);
}<|endoftext|>


<commit_before>(function(jQuery, window) {
	"use strict";

	var win = jQuery(window);
	var doc = jQuery(document);
	var rules = [];
	var rem = /(\d*\.?\d+)r?em/;
	var rpercent = /(\d*\.?\d+)%/;
	var width, height, scrollTop, scrollHeight, fontSize;

	var types = {
		number: function(n) { return n; },

		function: function(fn) { return fn(); },

		string: function(string) {
			var data, n;

			data = rem.exec(string);
			if (data) {
				n = parseFloat(data[1]);
				return getFontSize() * n;
			}

			data = rpercent.exec(string);
			if (data) {
				n = parseFloat(data[1]) / 100;
				return width * n;
			}

			throw new Error('[window.breakpoint] \'' + string + '\' cannot be parsed as rem, em or %.');
		}
	};

	var tests = {
		minWidth: function(value)  { return width >= types[typeof value](value); },
		maxWidth: function(value)  { return width <  types[typeof value](value); },
		minHeight: function(value) { return height >= types[typeof value](value); },
		maxHeight: function(value) { return height <  types[typeof value](value); },
		minScrollTop: function(value) { return scrollTop >= types[typeof value](value); },
		maxScrollTop: function(value) { return scrollTop <  types[typeof value](value); },
		minScrollBottom: function(value) { return (scrollHeight - height - scrollTop) >= types[typeof value](value); },
		maxScrollBottom: function(value) { return (scrollHeight - height - scrollTop) <  types[typeof value](value); }
	};

	function getStyle(node, name) {
		return window.getComputedStyle ?
			window
			.getComputedStyle(node, null)
			.getPropertyValue(name) :
			0 ;
	}

	function getFontSize() {
		return fontSize ||
			(fontSize = parseFloat(getStyle(document.documentElement, "font-size"), 10));
	}

	function media(query, fn1, fn2) {
		var rule = {};

		rule.query = query;
		rule.enter = fn1;
		rule.exit = fn2;
		rules.push(rule);

		return query;
	}

	function testProperty(property, value) {
		cutoffs[property]
	}

	function test(query) {
		var keys = Object.keys(query);
		var n = keys.length;
		var key;

		if (keys.length === 0) { return false; }

		while (n--) {
			key = keys[n];
			if (!tests[key](query[key])) { return false; }
		}

		return true;
	}

	function update() {
		var l = rules.length;
		var rule;

		// Run exiting rules
		while (l--) {
			rule = rules[l];

			if (rule.state && !test(rule.query)) {
				rule.state = false;
				rule.exit && rule.exit();
			}
		}

		l = rules.length;

		// Run entering rules
		while (l--) {
			rule = rules[l];

			if (!rule.state && test(rule.query)) {
				rule.state = true;
				rule.enter && rule.enter();
			}
		}
	}

	function scroll(e) {
		scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
		update();
	}

	function resize(e) {
		width = window.innerWidth;
		height = window.innerHeight;
		scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
		update();
	}

	win
	.on('scroll', scroll)
	.on('resize', resize);

	doc
	.ready(update)
	.on('DOMContentLoaded', update);

	width = window.innerWidth;
	height = window.innerHeight;
	scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;

	window.breakpoint = media;
})(jQuery, window);
<commit_msg>Linted<commit_after>(function(jQuery, window) {
	"use strict";

	var win = jQuery(window);
	var doc = jQuery(document);
	var rules = [];
	var rem = /(\d*\.?\d+)r?em/;
	var rpercent = /(\d*\.?\d+)%/;
	var width, height, scrollTop, scrollHeight, fontSize;

	var types = {
		number: function(n) { return n; },

		function: function(fn) { return fn(); },

		string: function(string) {
			var data, n;

			data = rem.exec(string);
			if (data) {
				n = parseFloat(data[1]);
				return getFontSize() * n;
			}

			data = rpercent.exec(string);
			if (data) {
				n = parseFloat(data[1]) / 100;
				return width * n;
			}

			throw new Error('[window.breakpoint] \'' + string + '\' cannot be parsed as rem, em or %.');
		}
	};

	var tests = {
		minWidth: function(value)  { return width >= types[typeof value](value); },
		maxWidth: function(value)  { return width <  types[typeof value](value); },
		minHeight: function(value) { return height >= types[typeof value](value); },
		maxHeight: function(value) { return height <  types[typeof value](value); },
		minScrollTop: function(value) { return scrollTop >= types[typeof value](value); },
		maxScrollTop: function(value) { return scrollTop <  types[typeof value](value); },
		minScrollBottom: function(value) { return (scrollHeight - height - scrollTop) >= types[typeof value](value); },
		maxScrollBottom: function(value) { return (scrollHeight - height - scrollTop) <  types[typeof value](value); }
	};

	function getStyle(node, name) {
		return window.getComputedStyle ?
			window
			.getComputedStyle(node, null)
			.getPropertyValue(name) :
			0 ;
	}

	function getFontSize() {
		return fontSize ||
			(fontSize = parseFloat(getStyle(document.documentElement, "font-size"), 10));
	}

	function media(query, fn1, fn2) {
		var rule = {};

		rule.query = query;
		rule.enter = fn1;
		rule.exit = fn2;
		rules.push(rule);

		return query;
	}

	function test(query) {
		var keys = Object.keys(query);
		var n = keys.length;
		var key;

		if (keys.length === 0) { return false; }

		while (n--) {
			key = keys[n];
			if (!tests[key](query[key])) { return false; }
		}

		return true;
	}

	function update() {
		var l = rules.length;
		var rule;

		// Run exiting rules
		while (l--) {
			rule = rules[l];

			if (rule.state && !test(rule.query)) {
				rule.state = false;
				rule.exit && rule.exit();
			}
		}

		l = rules.length;

		// Run entering rules
		while (l--) {
			rule = rules[l];

			if (!rule.state && test(rule.query)) {
				rule.state = true;
				rule.enter && rule.enter();
			}
		}
	}

	function scroll(e) {
		scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
		update();
	}

	function resize(e) {
		width = window.innerWidth;
		height = window.innerHeight;
		scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
		update();
	}

	win
	.on('scroll', scroll)
	.on('resize', resize);

	doc
	.ready(update)
	.on('DOMContentLoaded', update);

	width = window.innerWidth;
	height = window.innerHeight;
	scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
	scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;

	window.breakpoint = media;
})(jQuery, window);
<|endoftext|>


<commit_before>define(
  function(require, exports, module) {
    var squiggle = require("squiggle"),
        DashboardView = require("views/ui/DashboardView"),
        Screen = squiggle.views.screens.Screen,
        AnimationModel = squiggle.models.Animation,
        FrameModel = squiggle.models.Frame,
        RectangleView = squiggle.views.primitives.Rectangle,
        AppSettings = squiggle.models.AppSettings,
        ModalView = require("views/modal/ModalView"),
        FramesView = require("views/FramesView"),
        FrameCounterView = require("views/FrameCounterView"),
        PreviewView = require("views/PreviewView"),
        AnimationRenderView = squiggle.views.animation.AnimationRender,
        FrameCaptureView = squiggle.views.animation.FrameCapture,
        FrameRenderView = squiggle.views.animation.FrameRender,
        saveAs = require("saveAs"),
        Word = squiggle.views.text.Word,
        CookieUtils = require("utils/CookieUtils"),
        Button = squiggle.views.ui.Button,
        AnimationToolScreen = Screen.extend({

      /**
      * Array of Objects that will listen for onModelChange()
      */
      __modelChangeListeners : [],
      /**
      * Array of Objects that will listen for onFrameIndexChange()
      */
      __frameIndexListeners : [],
      /**
      * Current frame index
      */
      currentFrameIndex : 0,

      /**
      * Master Animation model
      */
      model : new AnimationModel(),

      /**
      * A FrameCapture instance to draw on
      */
      captureView : undefined,

      /**
      * Dashboard view contains play/pause button, add/remove frame buttons
      */
      dashBoardView : undefined,

      /**
      * View at the top that displays the current frame numebr
      */
      frameCounterView : undefined,

      /**
      * A modal view
      */
      modalView : undefined,

      /**
      * View that shows the frames being drawn
      */
      framesView : undefined,

      /**
      * A View for the animation Preview
      */
      previewView : undefined,

      /**
      * A Word instance with instructions for the user
      */
      instructionsWord : undefined,

      /**
      * A Timeout to jerkIt() with the instructions word
      */
      instructionsTimeout : undefined,

      /**
      * A view that shows a guide with the contents of the previous frame
      */
      onionSkinView : undefined,

      /**
      * first time use flag
      */
      ftu : true,

      /**
      * Button with about link
      */
      aboutButton : undefined,

      /**
      * Setup function
      */
      setup : function(){

        // background
        this.backgroundColor = "#EBF8FF";
        // setup app colors and other app-wide settings
        AppSettings.ButtonHeight = 80;
        AppSettings.UIMargin = 40;
        // disabled button color
        AppSettings.ButtonColorDisabled = "#CCCCCC";
        AppSettings.DarkBlueColor = "rgba(65,189,253,0.85)";
        // blue buttons
        AppSettings.ButtonColorNormalBlue = "#41BDFD";
        AppSettings.ButtonColorHoverBlue = "#50A9D9";
        AppSettings.ButtonColorDownBlue = "#285269";
        // red buttons
        AppSettings.ButtonColorNormalRed = "#FD4141";
        AppSettings.ButtonColorHoverRed = "#D95050";
        AppSettings.ButtonColorDownRed = "#582323";
        // green buttons
        AppSettings.ButtonColorNormalGreen = "#41FD72";
        AppSettings.ButtonColorHoverGreen = "#50D974";
        AppSettings.ButtonColorDownGreen = "#2A703C";
        // size for the animation frames
        AppSettings.AnimationSize = {width:480,height:320};
        // max amount of frames allowed
        AppSettings.maxFrames = 50;

        // frames view
        this.framesView = new FramesView();
        this.framesView.delegate = this;
        this.addSubview(this.framesView);
        // onion skin
        this.onionSkinView = new FrameRenderView()
                                                 .setWidth(AppSettings.AnimationSize.width)
                                                 .setHeight(AppSettings.AnimationSize.height)
                                                 .setStrokeColor('rgba(65,189,253,0.5)')
                                                 .setStrokeWeight(4)
                                                 .setOverrideLineStrokeProperties(true);
        this.addSubview(this.onionSkinView);

        // frame capture
        this.captureView = new FrameCaptureView()
                                                 .setWidth(AppSettings.AnimationSize.width)
                                                 .setHeight(AppSettings.AnimationSize.height);
        this.addSubview(this.captureView);
        // frame counter view
        this.frameCounterView = new FrameCounterView();
        this.addSubview(this.frameCounterView);
        // create and add the dashboard
        this.dashBoardView = new DashboardView();
        this.dashBoardView.hideAllButtons = true;
        this.dashBoardView.setDelegate(this);
        this.addSubview(this.dashBoardView);
        // instructions
        this.instructionsWord = new Word().setText('Squiggle here')
                                          .setFontSize(18)
                                          .setFontColor(AppSettings.ButtonColorDisabled)
                                          .setDefaultJerkinessBumpIncrease(0.5)
                                          .centerOnWindow();

        this.showInstuctionsView();
        // setup the modal view for messages
        this.modalView = new ModalView();
        this.addSubview(this.modalView);
        // read ftu cookie
        var ftucookie = CookieUtils.getCookie('ftu');
        if(ftucookie === undefined){
          this.ftu = true;
          this.dashBoardView.ftu = true;
          this.frameCounterView.ftu = true;
        }else{
          this.ftu = false;
          this.dashBoardView.ftu = false;
          this.frameCounterView.ftu = false;
          this.removeSubview(this.instructionsWord);
          this.instructionsWord = undefined;
        }
        // start animation
        var ff = new FrameModel();
        if(this.ftu){
          ff.on('change',function(){
            this.model.models[0].off('change');
            this.dashBoardView.showAddFrameButton = true;
            this.dashBoardView.showPreviewButton = false;
            this.dashBoardView.evaluateButtonsVisibility();
            window.clean = false;
          }.bind(this));
        }else{
          ff.on('change',function(){
            window.clean = false;
          }.bind(this));
        }
        this.model.add(ff);
        this.model.on('add remove',function(){
          this.__broadcastModelChange();
        }.bind(this));
        // add listeners
        this.addFrameIndexChangeListener(this.framesView, this.frameCounterView, this.dashBoardView);
        this.addModelChangelistener(this.framesView, this.frameCounterView, this.dashBoardView);
        this.__broadcastModelChange();
        this.__broadcastFrameIndexUpdate();
        // about SQUIGGLE ONE...
        this.aboutButton = new Button().setFontSize(8)
                                      .setText('squiggle one v1.0')
                                      .setShowUnderline(false)
                                      .setFontColorForState(AppSettings.ButtonColorNormalBlue,Button.states.NORMAL)
                                      .setFontColorForState(AppSettings.ButtonColorHoverBlue,Button.states.HOVER)
                                      .on(Button.events.CLICKED,function(){
                                        window.open("/about", "_blank");
                                      }.bind(this));
        this.aboutButton.getBackgroundRectangle().hidden = true;
        this.aboutButton.getWord().setStrokeWeight(1);
        this.addSubview(this.aboutButton);
        this.windowResized();
        window.clean = true;
      },

      windowResized : function(){
        Screen.prototype.windowResized.apply(this,arguments);
        this.captureView.setPosition((window.innerWidth/2) - (AppSettings.AnimationSize.width/2), (window.innerHeight/2) - (AppSettings.AnimationSize.height/2));
        this.onionSkinView.setPosition((window.innerWidth/2) - (AppSettings.AnimationSize.width/2), (window.innerHeight/2) - (AppSettings.AnimationSize.height/2));
        if(this.instructionsWord) this.instructionsWord.centerOnWindow();
        this.aboutButton.setPosition(window.innerWidth - 195,window.innerHeight - 24);
      },

      jerkItCallback : function(){
        if(!this.instructionsWord) return;
        this.instructionsTimeout = setTimeout(
          function(){
            if(this.instructionsWord) this.instructionsWord.jerkIt();
            this.jerkItCallback();
          }.bind(this),
          3000
        );
      },

      hideInstructionsView : function(){
        if(this.instructionsWord) this.instructionsWord.hidden = true;
        clearTimeout(this.instructionsTimeout);
      },

      showInstuctionsView : function(){
        this.addSubview(this.instructionsWord);
        this.instructionsWord.hidden = false;
        setTimeout(
          function(){
            if(this.instructionsWord) this.instructionsWord.jerkIt();
            this.jerkItCallback();
          }.bind(this),
          1000
        );
      },

      mouseDragged : function(){
        Screen.prototype.mouseDragged.apply(this,arguments);
        if(this.captureView.isMouseInBounds()){
          this.hideInstructionsView();
        }
      },

      keyPressed : function(){
        this.dashBoardView.keyPressed();
      },

      setCurrentFrameIndex : function(num){
        this.currentFrameIndex = num;
        this.__broadcastFrameIndexUpdate();
      },

      addFrameIndexChangeListener : function(){
        _.each(arguments, function(listener){
          this.__frameIndexListeners.push(listener);
        }.bind(this));
      },

      addModelChangelistener :function(){
        _.each(arguments, function(listener){
          this.__modelChangeListeners.push(listener);
        }.bind(this));
      },

      /**
      *Broadcasts frame index updates to the subscribed listeners
      */
      __broadcastFrameIndexUpdate : function(){
        _.each(this.__frameIndexListeners,function(listener){
          listener.onFrameIndexUpdate(this.currentFrameIndex);
        }.bind(this));
        this.onionSkinView.hidden = true;
      },

      /**
      *Broadcasts model change to the subscribed listeners
      */
      __broadcastModelChange : function(){
        _.each(this.__modelChangeListeners,function(listener){
          listener.onModelChange(this.model);
          window.clean = false;
        }.bind(this));
        this.captureView.frameRender = this.framesView.getCurrentFrameRender();

      },

      // DashboardView delegate methods
      onPlayPressed : function(){
        if(this.previewView === undefined) this.previewView = new PreviewView();
        this.previewView.delegate = this;
        this.previewView.start(this.model);
        this.addSubview(this.previewView);
        this.captureView.hidden = true;

      },
      onAddFramePressed: function(){
        if(this.model.models.length < AppSettings.maxFrames){
          var f = new FrameModel()
          this.model.models.splice(this.currentFrameIndex + 1, 0, f);
          this.model.trigger('add');
          this.setCurrentFrameIndex(this.currentFrameIndex+1);
          if(this.ftu){
            this.dashBoardView.showAddFrameButton = false;
            this.dashBoardView.evaluateButtonsVisibility();
            f.on('change',function(){
              f.off('change');
              this.dashBoardView.showPreviewButton = true;
              this.dashBoardView.evaluateButtonsVisibility();
            }.bind(this));
          }else{
            f.on('change',function(){
              f.off('change');
              window.clean = false;
            }.bind(this));
          }
        }
      },
      onRemoveFramePressed: function(){
        if(this.model.models.length > 1){
          this.modalView.showMessage("delete frame?","yes","no",function(){
            this.model.models.splice(this.currentFrameIndex, 1);
            this.model.trigger('remove');
            if(this.currentFrameIndex !== 0){
              this.setCurrentFrameIndex(this.currentFrameIndex-1);
            }else{
              this.setCurrentFrameIndex(0);
            }
          }.bind(this));
        }
      },
      onNextFramePressed : function(){
        if(this.currentFrameIndex < this.model.models.length - 1) this.setCurrentFrameIndex(this.currentFrameIndex + 1);
      },
      onPreviousFramePressed : function(){
        if(this.currentFrameIndex > 0) this.setCurrentFrameIndex(this.currentFrameIndex - 1);
      },
      onFrameTransitionComplete : function(){
        this.captureView.model = this.model.models[this.currentFrameIndex];
        if(this.currentFrameIndex > 0){
          this.onionSkinView.hidden = false;
          this.onionSkinView.model = this.model.models[this.currentFrameIndex-1];
        }
        if(this.ftu && this.currentFrameIndex !== 0){
          this.showInstuctionsView();
          this.ftu = false;
          CookieUtils.setCookie('ftu',false,365);
        }
      },
      // PreviewView delegate methods
      onClosePreview : function(){
        this.previewView.stop();
        this.removeSubview(this.previewView);
        this.captureView.hidden = false;
        this.removeSubview(this.instructionsWord);
        this.instructionsWord = undefined;
        this.dashBoardView.ftu = false;
        this.dashBoardView.evaluateButtonsVisibility();
      },
      onDownloadRequest : function(){
        this.onClosePreview();
        this.modalView.showProgress('Making a .gif');
        var renderer = new AnimationRenderView().setModel(this.model)
                                                .setWidth(AppSettings.AnimationSize.width*window.devicePixelRatio)
                                                .setHeight(AppSettings.AnimationSize.height*window.devicePixelRatio)
                                                .setWorkerScript('js/vendor/gif.worker.js')
                                                .setExportFrameDelay(100)
                                                .export(function(blob){
                                                  // open the animated gif on a new window
                                                  this.modalView.cancel();
                                                  saveAs(blob,'animation.gif');
                                                  //window.open(URL.createObjectURL(blob));
                                                }.bind(this));
        window.clean = true;
      }
    });
    return AnimationToolScreen;
  }
);
<commit_msg>Removes "about" page redirect<commit_after>define(
  function(require, exports, module) {
    var squiggle = require("squiggle"),
        DashboardView = require("views/ui/DashboardView"),
        Screen = squiggle.views.screens.Screen,
        AnimationModel = squiggle.models.Animation,
        FrameModel = squiggle.models.Frame,
        RectangleView = squiggle.views.primitives.Rectangle,
        AppSettings = squiggle.models.AppSettings,
        ModalView = require("views/modal/ModalView"),
        FramesView = require("views/FramesView"),
        FrameCounterView = require("views/FrameCounterView"),
        PreviewView = require("views/PreviewView"),
        AnimationRenderView = squiggle.views.animation.AnimationRender,
        FrameCaptureView = squiggle.views.animation.FrameCapture,
        FrameRenderView = squiggle.views.animation.FrameRender,
        saveAs = require("saveAs"),
        Word = squiggle.views.text.Word,
        CookieUtils = require("utils/CookieUtils"),
        Button = squiggle.views.ui.Button,
        AnimationToolScreen = Screen.extend({

      /**
      * Array of Objects that will listen for onModelChange()
      */
      __modelChangeListeners : [],
      /**
      * Array of Objects that will listen for onFrameIndexChange()
      */
      __frameIndexListeners : [],
      /**
      * Current frame index
      */
      currentFrameIndex : 0,

      /**
      * Master Animation model
      */
      model : new AnimationModel(),

      /**
      * A FrameCapture instance to draw on
      */
      captureView : undefined,

      /**
      * Dashboard view contains play/pause button, add/remove frame buttons
      */
      dashBoardView : undefined,

      /**
      * View at the top that displays the current frame numebr
      */
      frameCounterView : undefined,

      /**
      * A modal view
      */
      modalView : undefined,

      /**
      * View that shows the frames being drawn
      */
      framesView : undefined,

      /**
      * A View for the animation Preview
      */
      previewView : undefined,

      /**
      * A Word instance with instructions for the user
      */
      instructionsWord : undefined,

      /**
      * A Timeout to jerkIt() with the instructions word
      */
      instructionsTimeout : undefined,

      /**
      * A view that shows a guide with the contents of the previous frame
      */
      onionSkinView : undefined,

      /**
      * first time use flag
      */
      ftu : true,

      /**
      * Button with about link
      */
      aboutButton : undefined,

      /**
      * Setup function
      */
      setup : function(){

        // background
        this.backgroundColor = "#EBF8FF";
        // setup app colors and other app-wide settings
        AppSettings.ButtonHeight = 80;
        AppSettings.UIMargin = 40;
        // disabled button color
        AppSettings.ButtonColorDisabled = "#CCCCCC";
        AppSettings.DarkBlueColor = "rgba(65,189,253,0.85)";
        // blue buttons
        AppSettings.ButtonColorNormalBlue = "#41BDFD";
        AppSettings.ButtonColorHoverBlue = "#50A9D9";
        AppSettings.ButtonColorDownBlue = "#285269";
        // red buttons
        AppSettings.ButtonColorNormalRed = "#FD4141";
        AppSettings.ButtonColorHoverRed = "#D95050";
        AppSettings.ButtonColorDownRed = "#582323";
        // green buttons
        AppSettings.ButtonColorNormalGreen = "#41FD72";
        AppSettings.ButtonColorHoverGreen = "#50D974";
        AppSettings.ButtonColorDownGreen = "#2A703C";
        // size for the animation frames
        AppSettings.AnimationSize = {width:480,height:320};
        // max amount of frames allowed
        AppSettings.maxFrames = 50;

        // frames view
        this.framesView = new FramesView();
        this.framesView.delegate = this;
        this.addSubview(this.framesView);
        // onion skin
        this.onionSkinView = new FrameRenderView()
                                                 .setWidth(AppSettings.AnimationSize.width)
                                                 .setHeight(AppSettings.AnimationSize.height)
                                                 .setStrokeColor('rgba(65,189,253,0.5)')
                                                 .setStrokeWeight(4)
                                                 .setOverrideLineStrokeProperties(true);
        this.addSubview(this.onionSkinView);

        // frame capture
        this.captureView = new FrameCaptureView()
                                                 .setWidth(AppSettings.AnimationSize.width)
                                                 .setHeight(AppSettings.AnimationSize.height);
        this.addSubview(this.captureView);
        // frame counter view
        this.frameCounterView = new FrameCounterView();
        this.addSubview(this.frameCounterView);
        // create and add the dashboard
        this.dashBoardView = new DashboardView();
        this.dashBoardView.hideAllButtons = true;
        this.dashBoardView.setDelegate(this);
        this.addSubview(this.dashBoardView);
        // instructions
        this.instructionsWord = new Word().setText('Squiggle here')
                                          .setFontSize(18)
                                          .setFontColor(AppSettings.ButtonColorDisabled)
                                          .setDefaultJerkinessBumpIncrease(0.5)
                                          .centerOnWindow();

        this.showInstuctionsView();
        // setup the modal view for messages
        this.modalView = new ModalView();
        this.addSubview(this.modalView);
        // read ftu cookie
        var ftucookie = CookieUtils.getCookie('ftu');
        if(ftucookie === undefined){
          this.ftu = true;
          this.dashBoardView.ftu = true;
          this.frameCounterView.ftu = true;
        }else{
          this.ftu = false;
          this.dashBoardView.ftu = false;
          this.frameCounterView.ftu = false;
          this.removeSubview(this.instructionsWord);
          this.instructionsWord = undefined;
        }
        // start animation
        var ff = new FrameModel();
        if(this.ftu){
          ff.on('change',function(){
            this.model.models[0].off('change');
            this.dashBoardView.showAddFrameButton = true;
            this.dashBoardView.showPreviewButton = false;
            this.dashBoardView.evaluateButtonsVisibility();
            window.clean = false;
          }.bind(this));
        }else{
          ff.on('change',function(){
            window.clean = false;
          }.bind(this));
        }
        this.model.add(ff);
        this.model.on('add remove',function(){
          this.__broadcastModelChange();
        }.bind(this));
        // add listeners
        this.addFrameIndexChangeListener(this.framesView, this.frameCounterView, this.dashBoardView);
        this.addModelChangelistener(this.framesView, this.frameCounterView, this.dashBoardView);
        this.__broadcastModelChange();
        this.__broadcastFrameIndexUpdate();
        // about SQUIGGLE ONE...
        this.aboutButton = new Button().setFontSize(8)
                                      .setText('squiggle one v1.0')
                                      .setShowUnderline(false)
                                      .setFontColorForState(AppSettings.ButtonColorNormalBlue,Button.states.NORMAL)
                                      .setFontColorForState(AppSettings.ButtonColorHoverBlue,Button.states.HOVER);
        this.aboutButton.getBackgroundRectangle().hidden = true;
        this.aboutButton.getWord().setStrokeWeight(1);
        this.addSubview(this.aboutButton);
        this.windowResized();
        window.clean = true;
      },

      windowResized : function(){
        Screen.prototype.windowResized.apply(this,arguments);
        this.captureView.setPosition((window.innerWidth/2) - (AppSettings.AnimationSize.width/2), (window.innerHeight/2) - (AppSettings.AnimationSize.height/2));
        this.onionSkinView.setPosition((window.innerWidth/2) - (AppSettings.AnimationSize.width/2), (window.innerHeight/2) - (AppSettings.AnimationSize.height/2));
        if(this.instructionsWord) this.instructionsWord.centerOnWindow();
        this.aboutButton.setPosition(window.innerWidth - 195,window.innerHeight - 24);
      },

      jerkItCallback : function(){
        if(!this.instructionsWord) return;
        this.instructionsTimeout = setTimeout(
          function(){
            if(this.instructionsWord) this.instructionsWord.jerkIt();
            this.jerkItCallback();
          }.bind(this),
          3000
        );
      },

      hideInstructionsView : function(){
        if(this.instructionsWord) this.instructionsWord.hidden = true;
        clearTimeout(this.instructionsTimeout);
      },

      showInstuctionsView : function(){
        this.addSubview(this.instructionsWord);
        this.instructionsWord.hidden = false;
        setTimeout(
          function(){
            if(this.instructionsWord) this.instructionsWord.jerkIt();
            this.jerkItCallback();
          }.bind(this),
          1000
        );
      },

      mouseDragged : function(){
        Screen.prototype.mouseDragged.apply(this,arguments);
        if(this.captureView.isMouseInBounds()){
          this.hideInstructionsView();
        }
      },

      keyPressed : function(){
        this.dashBoardView.keyPressed();
      },

      setCurrentFrameIndex : function(num){
        this.currentFrameIndex = num;
        this.__broadcastFrameIndexUpdate();
      },

      addFrameIndexChangeListener : function(){
        _.each(arguments, function(listener){
          this.__frameIndexListeners.push(listener);
        }.bind(this));
      },

      addModelChangelistener :function(){
        _.each(arguments, function(listener){
          this.__modelChangeListeners.push(listener);
        }.bind(this));
      },

      /**
      *Broadcasts frame index updates to the subscribed listeners
      */
      __broadcastFrameIndexUpdate : function(){
        _.each(this.__frameIndexListeners,function(listener){
          listener.onFrameIndexUpdate(this.currentFrameIndex);
        }.bind(this));
        this.onionSkinView.hidden = true;
      },

      /**
      *Broadcasts model change to the subscribed listeners
      */
      __broadcastModelChange : function(){
        _.each(this.__modelChangeListeners,function(listener){
          listener.onModelChange(this.model);
          window.clean = false;
        }.bind(this));
        this.captureView.frameRender = this.framesView.getCurrentFrameRender();

      },

      // DashboardView delegate methods
      onPlayPressed : function(){
        if(this.previewView === undefined) this.previewView = new PreviewView();
        this.previewView.delegate = this;
        this.previewView.start(this.model);
        this.addSubview(this.previewView);
        this.captureView.hidden = true;

      },
      onAddFramePressed: function(){
        if(this.model.models.length < AppSettings.maxFrames){
          var f = new FrameModel()
          this.model.models.splice(this.currentFrameIndex + 1, 0, f);
          this.model.trigger('add');
          this.setCurrentFrameIndex(this.currentFrameIndex+1);
          if(this.ftu){
            this.dashBoardView.showAddFrameButton = false;
            this.dashBoardView.evaluateButtonsVisibility();
            f.on('change',function(){
              f.off('change');
              this.dashBoardView.showPreviewButton = true;
              this.dashBoardView.evaluateButtonsVisibility();
            }.bind(this));
          }else{
            f.on('change',function(){
              f.off('change');
              window.clean = false;
            }.bind(this));
          }
        }
      },
      onRemoveFramePressed: function(){
        if(this.model.models.length > 1){
          this.modalView.showMessage("delete frame?","yes","no",function(){
            this.model.models.splice(this.currentFrameIndex, 1);
            this.model.trigger('remove');
            if(this.currentFrameIndex !== 0){
              this.setCurrentFrameIndex(this.currentFrameIndex-1);
            }else{
              this.setCurrentFrameIndex(0);
            }
          }.bind(this));
        }
      },
      onNextFramePressed : function(){
        if(this.currentFrameIndex < this.model.models.length - 1) this.setCurrentFrameIndex(this.currentFrameIndex + 1);
      },
      onPreviousFramePressed : function(){
        if(this.currentFrameIndex > 0) this.setCurrentFrameIndex(this.currentFrameIndex - 1);
      },
      onFrameTransitionComplete : function(){
        this.captureView.model = this.model.models[this.currentFrameIndex];
        if(this.currentFrameIndex > 0){
          this.onionSkinView.hidden = false;
          this.onionSkinView.model = this.model.models[this.currentFrameIndex-1];
        }
        if(this.ftu && this.currentFrameIndex !== 0){
          this.showInstuctionsView();
          this.ftu = false;
          CookieUtils.setCookie('ftu',false,365);
        }
      },
      // PreviewView delegate methods
      onClosePreview : function(){
        this.previewView.stop();
        this.removeSubview(this.previewView);
        this.captureView.hidden = false;
        this.removeSubview(this.instructionsWord);
        this.instructionsWord = undefined;
        this.dashBoardView.ftu = false;
        this.dashBoardView.evaluateButtonsVisibility();
      },
      onDownloadRequest : function(){
        this.onClosePreview();
        this.modalView.showProgress('Making a .gif');
        var renderer = new AnimationRenderView().setModel(this.model)
                                                .setWidth(AppSettings.AnimationSize.width*window.devicePixelRatio)
                                                .setHeight(AppSettings.AnimationSize.height*window.devicePixelRatio)
                                                .setWorkerScript('js/vendor/gif.worker.js')
                                                .setExportFrameDelay(100)
                                                .export(function(blob){
                                                  // open the animated gif on a new window
                                                  this.modalView.cancel();
                                                  saveAs(blob,'animation.gif');
                                                  //window.open(URL.createObjectURL(blob));
                                                }.bind(this));
        window.clean = true;
      }
    });
    return AnimationToolScreen;
  }
);
<|endoftext|>


<commit_before>dojo.declare( 'JBrowse.Model.TrackMetaData', null,
/**
 * @lends JBrowse.Model.TrackMetaData.prototype
 */
{
    /**
     * Data store for track metadata, supporting faceted
     * (parameterized) searching.  Keeps all of the track metadata,
     * and the indexes thereof, in memory.
     * @constructs
     * @param args.trackConfigs {Array} array of track configuration
     */
    constructor: function( args ) {

        // set up our facet name discrimination: what facets we will
        // actually provide search on
        var ident_attrs = this.getIdentityAttributes();
        this._filterFacet = function( facetName ) {
            var userfilter = args.filterFacets || function() {return true;};
            return userfilter(facetName)
                   && ! dojo.some( ident_attrs, function(i) { return i == facetName;});
        };

        // set up our onReady callbacks
        if( ! dojo.isArray( args.onReady ) ){
            this.onReadyFuncs = args.onReady ? [ args.onReady ] : [];
        } else {
            this.onReadyFuncs = dojo.clone(args.onReady);
        }

        // index the track configurations as a store
        this._indexItems(
            {
                store: this,
                items: dojo.map( args.trackConfigs, function(conf) {
                    var metarecord = conf.metadata || {};
                    metarecord.label = conf.label;
                    metarecord.key = conf.key;
                    return metarecord;
                })
            }
        );

        // fetch and index all the items from each of the stores
        var stores_fetched_count = 0;
        dojo.forEach( args.metadataStores, function(store) {
            store.fetch({
                scope: this,
                onComplete: Util.debugHandler( this, function(items) {
                    // build our indexes
                    this._indexItems({ store: store, items: items, supplementalOnly: true });

                    // if this is the last store to be fetched, call
                    // our onReady callbacks
                    if( ++stores_fetched_count == args.metadataStores.length ) {
                        this.ready = true;
                        dojo.forEach( this.onReadyFuncs, function(f) {
                                          f.call( this, this );
                                      }, this );
                        this.onReadyFuncs = [];
                    }
                })
            });
        },this);
     },

    _indexItems: function( args ) {
        // get our (filtered) list of facets we will index for
        var store = args.store,
            items = args.items;

        // convert the items to a uniform format
        items = dojo.map( items, function( item ) {
                              var attributes = store.getAttributes(item);

                              //convert the item into a uniform data format of plain objects
                              var newitem = {};
                              dojo.forEach( attributes, function(attr) {
                                                newitem[attr] = store.getValue(item,attr);
                                            });
                              return newitem;
                          },
                          this
                        );

        // merge them with any existing records, filtering out ones
        // that should be ignored if we were passed
        // 'supplementalOnly', and update the identity index
        this.identIndex = this.identIndex || {};
        items = dojo.filter( items, function(item) {
                                 // merge the new item attributes with any existing
                                 // record for this item
                                 var ident = this.getIdentity(item);
                                 var existingItem = this.identIndex[ ident ];
                                 if( args.supplementalOnly && !existingItem) {
                                     // skip this item if we are supplementalOnly and it
                                     // does not already exist
                                     return false;
                                 }
                                 this.identIndex[ ident ] = dojo.mixin( existingItem || {}, item );
                                 return true;
                             },
                             this
                           );

        // update our facet list to include any new attrs these
        // items have
        var old_facets = this.facets || [];
        this.facets = (function() {
            var seen = {};
            return dojo.filter( old_facets.concat( this.getAttributes( items[0] ) ),
                                function(facetName) {
                                    var take = this._filterFacet(facetName) && !seen[facetName];
                                    seen[facetName] = true;
                                    return take;
                                },
                                this
                              );
        }).call(this);
        var new_facets = this.facets.slice( old_facets.length );

        // initialize indexes for any new facets
        this.facetIndexes = this.facetIndexes || { itemCount: 0, bucketCount: 0, byName: {} };
        dojo.forEach( new_facets, function(facet) {
            if( ! this.facetIndexes.byName[facet] ) {
                this.facetIndexes.bucketCount++;
                this.facetIndexes.byName[facet] = { itemCount: 0, bucketCount: 0, byValue: {} };
            }
        }, this);

        // now update the indexes with the new facets
        if( new_facets.length ) {
            dojo.forEach( items, function( item ) {
                this.facetIndexes.itemCount++;
                dojo.forEach( new_facets, function( facet ) {

                    var value = this.getValue( item, facet, undefined );
                    if( typeof value == 'undefined' )
                        return;
                    var facetValues = this.facetIndexes.byName[facet];
                    var bucket = facetValues.byValue[value];
                    if( !bucket ) {
                        bucket = facetValues.byValue[value] = { itemCount: 0, items: [] };
                        facetValues.bucketCount++;
                    }
                    bucket.itemCount++;
                    bucket.items.push(item);
                },this);
            }, this);

            // calculate the rank of the facets: make an array of
            // facet names sorted by smallest average bucket size,
            // descending
            this.facetIndexes.facetRank = this.facets.sort(dojo.hitch(this,function(a,b){
                a = this.facetIndexes.byName[a];
                b = this.facetIndexes.byName[b];
                return b.itemCount/b.bucketCount - a.itemCount/a.bucketCount;
            }));
        }

        console.log(this.facetIndexes);
    },

    /**
     * Get an array of the text names of the facets that are defined
     * in this track metadata.
     * @param callback {Function} called as callback( [facet,facet,...] )
     */
    getFacets: function( callback ) {
        return this.facets;
    },

    /**
     * Get an Array of the distinct values for a given facet name.
     * @param facetName {String} the facet name
     * @returns {Array} distinct values for that facet
     */
    getFacetValues: function( facetName ) {
        var index = this.facetIndexes.byName[facetName];
        if( !index )
            return [];

        return dojof.keys( index.byValue );
    },

    /**
     * Add a callback to be called when this store is ready (i.e. loaded).
     */
    onReady: function( callback ) {
        if( this.ready ) {
            callback.call( this, this );
        }
        else {
            this.onReadyFuncs.push( callback );
        }
    },

    // dojo.data.api.Read support

    getValue: function( i, attr, defaultValue ) {
        var v = i[attr];
        return typeof v == 'undefined' ? defaultValue : v;
    },
    getValues: function( i, attr ) {
        var a = [ i[attr] ];
        return typeof a[0] == 'undefined' ? [] : a;
    },

    getAttributes: function(item)  {
        return dojof.keys( item );
    },

    hasAttribute: function(item,attr) {
        return item.hasOwnProperty(attr);
    },

    containsValue: function(item, attribute, value) {
        return item[attribute] == value;
    },

    isItem: function(item) {
        return typeof item == 'object' && typeof item[name] == 'string';
    },

    isItemLoaded: function() {
        return true;
    },

    loadItem: function( args ) {
    },

    // used by the dojo.data.util.simpleFetch mixin to implement fetch()
    _fetchItems: function( keywordArgs, findCallback, errorCallback ) {
        if( ! this.ready ) {
            this.onReady( dojo.hitch( this, '_fetchItems', keywordArgs, findCallback, errorCallback ) );
            return;
        }

        var query = dojo.clone( keywordArgs.query || {} );
        var textFilter = query.text;
        delete query.text;

        var results;

        // if we don't actually have any facets specified in the
        // query, the results are just all the items
        if( ! dojo.some( dojof.values(query), function(q){ return q.length > 0;}) ) {
            results = dojof.values( this.identIndex );
        }
        else {
            // start with an initial set of items that have the desired
            // value for the most specific (smallest avg index bucket
            // size) facet that was specified
            results = (function() {
                           var highestRankedFacet,
                               queryValues = [],
                               index;
                           dojo.some( this.facetIndexes.facetRank, function(facetName) {
                                          if( query[facetName] ) {
                                              highestRankedFacet = facetName;
                                              queryValues = query[highestRankedFacet];
                                              index = this.facetIndexes.byName[highestRankedFacet];
                                              return queryValues.length > 0;
                                          }
                                          return false;
                                      },this);

                           delete query[highestRankedFacet];

                           var set = [];
                           dojo.forEach( queryValues, function(val) {
                              var bucket = index.byValue[val];
                              if( bucket )
                                  set.push.apply( set, bucket.items );
                           },this);
                           return set;
                       }).call(this);

            // and filter this starting set for the other facets
            dojo.forEach( dojof.keys(query), function(facetName) {
                              var desired_values = query[facetName] || [];
                              if( ! desired_values.length )
                                  return;
                              results = dojo.filter(results, function(item) {
                                                        var value = this.getValue(item,facetName);
                                                        return dojo.some( desired_values, function(desired) {
                                                                              return desired == value;
                                                                          },this);
                                                    },this);
                          },this);
        }

        // TODO: filter with the text filter, if we have it

        // and finally, hand them to the finding callback
        findCallback(results,keywordArgs);
    },

    getFeatures: function() {
        return {
	    'dojo.data.api.Read': true,
	    'dojo.data.api.Identity': true
	};
    },
    close: function() {},

    getLabel: function(i) {
        return this.getValue(i,'key',undefined);
    },
    getLabelAttributes: function(i) {
        return ['key']; },

    // dojo.data.api.Identity support
    getIdentityAttributes: function() {
        return ['label'];
    },
    getIdentity: function(i) {
        return this.getValue(i, 'label', undefined);
    },
    fetchItemByIdentity: function(id) {
        return this.identIndex[id];
    }
});
dojo.require('dojo.data.util.simpleFetch');
dojo.extend( JBrowse.Model.TrackMetaData, dojo.data.util.simpleFetch );<commit_msg>put a copy of the track conf in the trackmetadata item for convenience<commit_after>dojo.declare( 'JBrowse.Model.TrackMetaData', null,
/**
 * @lends JBrowse.Model.TrackMetaData.prototype
 */
{
    /**
     * Data store for track metadata, supporting faceted
     * (parameterized) searching.  Keeps all of the track metadata,
     * and the indexes thereof, in memory.
     * @constructs
     * @param args.trackConfigs {Array} array of track configuration
     */
    constructor: function( args ) {

        // set up our facet name discrimination: what facets we will
        // actually provide search on
        var non_facet_attrs = this.getIdentityAttributes();
        non_facet_attrs.push('conf');
        this._filterFacet = function( facetName ) {
            var userfilter = args.filterFacets || function() {return true;};
            return userfilter(facetName)
                   && ! dojo.some( non_facet_attrs, function(a) { return a == facetName;});
        };

        // set up our onReady callbacks
        if( ! dojo.isArray( args.onReady ) ){
            this.onReadyFuncs = args.onReady ? [ args.onReady ] : [];
        } else {
            this.onReadyFuncs = dojo.clone(args.onReady);
        }

        // index the track configurations as a store
        this._indexItems(
            {
                store: this,
                items: dojo.map( args.trackConfigs, function(conf) {
                    var metarecord = dojo.clone( conf.metadata || {} );
                    metarecord.label = conf.label;
                    metarecord.key = conf.key;
                    metarecord.conf = conf;
                    return metarecord;
                })
            }
        );

        // fetch and index all the items from each of the stores
        var stores_fetched_count = 0;
        dojo.forEach( args.metadataStores, function(store) {
            store.fetch({
                scope: this,
                onComplete: Util.debugHandler( this, function(items) {
                    // build our indexes
                    this._indexItems({ store: store, items: items, supplementalOnly: true });

                    // if this is the last store to be fetched, call
                    // our onReady callbacks
                    if( ++stores_fetched_count == args.metadataStores.length ) {
                        this.ready = true;
                        dojo.forEach( this.onReadyFuncs, function(f) {
                                          f.call( this, this );
                                      }, this );
                        this.onReadyFuncs = [];
                    }
                })
            });
        },this);
     },

    _indexItems: function( args ) {
        // get our (filtered) list of facets we will index for
        var store = args.store,
            items = args.items;

        // convert the items to a uniform format
        items = dojo.map( items, function( item ) {
                              var attributes = store.getAttributes(item);

                              //convert the item into a uniform data format of plain objects
                              var newitem = {};
                              dojo.forEach( attributes, function(attr) {
                                                newitem[attr] = store.getValue(item,attr);
                                            });
                              return newitem;
                          },
                          this
                        );

        // merge them with any existing records, filtering out ones
        // that should be ignored if we were passed
        // 'supplementalOnly', and update the identity index
        this.identIndex = this.identIndex || {};
        items = dojo.filter( items, function(item) {
                                 // merge the new item attributes with any existing
                                 // record for this item
                                 var ident = this.getIdentity(item);
                                 var existingItem = this.identIndex[ ident ];
                                 if( args.supplementalOnly && !existingItem) {
                                     // skip this item if we are supplementalOnly and it
                                     // does not already exist
                                     return false;
                                 }
                                 this.identIndex[ ident ] = dojo.mixin( existingItem || {}, item );
                                 return true;
                             },
                             this
                           );

        // update our facet list to include any new attrs these
        // items have
        var old_facets = this.facets || [];
        this.facets = (function() {
            var seen = {};
            return dojo.filter( old_facets.concat( this.getAttributes( items[0] ) ),
                                function(facetName) {
                                    var take = this._filterFacet(facetName) && !seen[facetName];
                                    seen[facetName] = true;
                                    return take;
                                },
                                this
                              );
        }).call(this);
        var new_facets = this.facets.slice( old_facets.length );

        // initialize indexes for any new facets
        this.facetIndexes = this.facetIndexes || { itemCount: 0, bucketCount: 0, byName: {} };
        dojo.forEach( new_facets, function(facet) {
            if( ! this.facetIndexes.byName[facet] ) {
                this.facetIndexes.bucketCount++;
                this.facetIndexes.byName[facet] = { itemCount: 0, bucketCount: 0, byValue: {} };
            }
        }, this);

        // now update the indexes with the new facets
        if( new_facets.length ) {
            dojo.forEach( items, function( item ) {
                this.facetIndexes.itemCount++;
                dojo.forEach( new_facets, function( facet ) {

                    var value = this.getValue( item, facet, undefined );
                    if( typeof value == 'undefined' )
                        return;
                    var facetValues = this.facetIndexes.byName[facet];
                    var bucket = facetValues.byValue[value];
                    if( !bucket ) {
                        bucket = facetValues.byValue[value] = { itemCount: 0, items: [] };
                        facetValues.bucketCount++;
                    }
                    bucket.itemCount++;
                    bucket.items.push(item);
                },this);
            }, this);

            // calculate the rank of the facets: make an array of
            // facet names sorted by smallest average bucket size,
            // descending
            this.facetIndexes.facetRank = this.facets.sort(dojo.hitch(this,function(a,b){
                a = this.facetIndexes.byName[a];
                b = this.facetIndexes.byName[b];
                return b.itemCount/b.bucketCount - a.itemCount/a.bucketCount;
            }));
        }

        console.log(this.facetIndexes);
    },

    /**
     * Get an array of the text names of the facets that are defined
     * in this track metadata.
     * @param callback {Function} called as callback( [facet,facet,...] )
     */
    getFacets: function( callback ) {
        return this.facets;
    },

    /**
     * Get an Array of the distinct values for a given facet name.
     * @param facetName {String} the facet name
     * @returns {Array} distinct values for that facet
     */
    getFacetValues: function( facetName ) {
        var index = this.facetIndexes.byName[facetName];
        if( !index )
            return [];

        return dojof.keys( index.byValue );
    },

    /**
     * Add a callback to be called when this store is ready (i.e. loaded).
     */
    onReady: function( callback ) {
        if( this.ready ) {
            callback.call( this, this );
        }
        else {
            this.onReadyFuncs.push( callback );
        }
    },

    // dojo.data.api.Read support

    getValue: function( i, attr, defaultValue ) {
        var v = i[attr];
        return typeof v == 'undefined' ? defaultValue : v;
    },
    getValues: function( i, attr ) {
        var a = [ i[attr] ];
        return typeof a[0] == 'undefined' ? [] : a;
    },

    getAttributes: function(item)  {
        return dojof.keys( item );
    },

    hasAttribute: function(item,attr) {
        return item.hasOwnProperty(attr);
    },

    containsValue: function(item, attribute, value) {
        return item[attribute] == value;
    },

    isItem: function(item) {
        return typeof item == 'object' && typeof item[name] == 'string';
    },

    isItemLoaded: function() {
        return true;
    },

    loadItem: function( args ) {
    },

    // used by the dojo.data.util.simpleFetch mixin to implement fetch()
    _fetchItems: function( keywordArgs, findCallback, errorCallback ) {
        if( ! this.ready ) {
            this.onReady( dojo.hitch( this, '_fetchItems', keywordArgs, findCallback, errorCallback ) );
            return;
        }

        var query = dojo.clone( keywordArgs.query || {} );
        var textFilter = query.text;
        delete query.text;

        var results;

        // if we don't actually have any facets specified in the
        // query, the results are just all the items
        if( ! dojo.some( dojof.values(query), function(q){ return q.length > 0;}) ) {
            results = dojof.values( this.identIndex );
        }
        else {
            // start with an initial set of items that have the desired
            // value for the most specific (smallest avg index bucket
            // size) facet that was specified
            results = (function() {
                           var highestRankedFacet,
                               queryValues = [],
                               index;
                           dojo.some( this.facetIndexes.facetRank, function(facetName) {
                                          if( query[facetName] ) {
                                              highestRankedFacet = facetName;
                                              queryValues = query[highestRankedFacet];
                                              index = this.facetIndexes.byName[highestRankedFacet];
                                              return queryValues.length > 0;
                                          }
                                          return false;
                                      },this);

                           delete query[highestRankedFacet];

                           var set = [];
                           dojo.forEach( queryValues, function(val) {
                              var bucket = index.byValue[val];
                              if( bucket )
                                  set.push.apply( set, bucket.items );
                           },this);
                           return set;
                       }).call(this);

            // and filter this starting set for the other facets
            dojo.forEach( dojof.keys(query), function(facetName) {
                              var desired_values = query[facetName] || [];
                              if( ! desired_values.length )
                                  return;
                              results = dojo.filter(results, function(item) {
                                                        var value = this.getValue(item,facetName);
                                                        return dojo.some( desired_values, function(desired) {
                                                                              return desired == value;
                                                                          },this);
                                                    },this);
                          },this);
        }

        // TODO: filter with the text filter, if we have it

        // and finally, hand them to the finding callback
        findCallback(results,keywordArgs);
    },

    getFeatures: function() {
        return {
	    'dojo.data.api.Read': true,
	    'dojo.data.api.Identity': true
	};
    },
    close: function() {},

    getLabel: function(i) {
        return this.getValue(i,'key',undefined);
    },
    getLabelAttributes: function(i) {
        return ['key']; },

    // dojo.data.api.Identity support
    getIdentityAttributes: function() {
        return ['label'];
    },
    getIdentity: function(i) {
        return this.getValue(i, 'label', undefined);
    },
    fetchItemByIdentity: function(id) {
        return this.identIndex[id];
    }
});
dojo.require('dojo.data.util.simpleFetch');
dojo.extend( JBrowse.Model.TrackMetaData, dojo.data.util.simpleFetch );<|endoftext|>


<commit_before>'use strict';

var $ = require('jquery'),
    TimePickerComponent = require('./TimePickerComponent');

require('../libs/pikaday/plugins/pikaday.jquery');
require('../libs/select2/dist/js/select2.min');
require('../libs/spectrum/spectrum');

function PulsarFormComponent(html) {
    this.$html = html;
    this.timePickerComponent = new TimePickerComponent();
}

PulsarFormComponent.prototype.init = function () {
    var component = this;

    // Colour picker
    component.initColourpickers();

    // Date picker
    component.initDatePickers();

    // Choice block
    component.initSelectionButtons();

    // Select2
    component.initSelect2(component.$html.find('.js-select2:not([data-init="false"])'));

    // Time picker
    component.initTimePickers();

    // reinitialise select2 items in a tab when the tab is focused to fix widths
    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
        var $target = $($(e.target).attr('href')),
            $select2 = $target.find('.js-select2:not([data-init="false"])');

        $.each($select2, function() {
            component.initSelect2($(this));
        });
    });

    // reinitialise select2 items when opening a modal to fix widths
    $('[data-toggle="modal"]').on('click', function (e) {
        var $target = $($(e.target).attr('href')),
            $select2 = $target.find('.js-select2:not([data-init="false"])');

        $target.on('shown.bs.modal', function() {
            $.each($select2, function() {
                component.initSelect2($(this));
            });
        });
    });
}

/**
 * Re-initialise the Form Component Services
 * I've intentionally left out Select2 here, as programmatic
 * control often requires more custom behaviour than simply re-init'ing
 */
PulsarFormComponent.prototype.refresh = function () {
    // Colour picker
    this.initColourpickers();

    // Date picker
    this.initDatePickers();

    // Choice block
    this.initSelectionButtons();

    // Time picker
    this.initTimePickers();
}

/**
 * Initiate a time picker on data-timepicker fields
 */
PulsarFormComponent.prototype.initTimePickers = function () {
    this.timePickerComponent.init(this.$html.find('[data-timepicker=true]'));
}

/**
 * Initiate a date picker on data-datepicker fields using pickaday
 */
PulsarFormComponent.prototype.initDatePickers = function () {
    // Attach basic pikaday to datepicker fields
    this.$html
        .find('[data-datepicker=true]')
        .pikaday({ format: 'DD/MM/YYYY' });
}

/**
 * Initiate colour pickers
 */
PulsarFormComponent.prototype.initColourpickers = function () {
    var component = this,
        pickers = component.$html.find('.js-colorpicker');

    pickers.each(function() {

        var $this = $(this),
            $input = $this.find('.form__control'),
            $pickerInput = $('<input data-colour-picker-input>'),
            disabledAttr = $input.attr('disabled'),
            isDisabled = false,
            existingPicker = $input.next('[data-colour-picker-input]');

        if (typeof disabledAttr !== typeof undefined && disabledAttr !== false) {
            isDisabled = true;
        }

        if (existingPicker.length) {
            // Remove existing colour picker elements,
            // frustratingly invoking the spectrum 'destroy'
            // method does not remove the sp-replacer
            $pickerInput.spectrum('destroy');
            existingPicker.remove();
            $input.siblings('.sp-replacer').remove();
        }

        $pickerInput.insertAfter($input);

        // changing the picker should update the input
        $pickerInput.spectrum({
            color: '#' + $input.val(),
            disabled: isDisabled,
            showInput: false,
            preferredFormat: 'hex',
            replacerClassName: 'btn',
            change: function (color) {
                if (!$input.attr('disabled')) {
                    $input.val(('' + color).substring(1));
                    $input.trigger('change');
                }
            }
        });

        // changing the input should update the picker
        $input.on('change', function () {
            $pickerInput.spectrum('set', '#' + $input.val());
        });
    });
}

/**
 * Update colour pickers within a scope
 * @param $root
 */
PulsarFormComponent.prototype.updateColourPicker = function ($root) {
    const $input = $root.find('.js-colorpicker .form__control');

    $input.each((index, element) => {
        const $input = $(element);
        const $picker = $input.next();

        $picker.spectrum('set', `#${$input.val()}`);
    });
}

/**
 * Initiate Selection Buttons
 */
PulsarFormComponent.prototype.initSelectionButtons = function () {
    const choiceblock = this.$html.find('.choice--block');

    choiceblock
        .find('input[type="checkbox"]:checked, input[type="radio"]:checked')
        .closest('.control__label')
        .addClass('is-selected');

    choiceblock
        .on('change', '.controls input[type="checkbox"], .controls input[type="radio"]', this.selectionButtons);
}

/**
 * Selection Button className logic
 */
PulsarFormComponent.prototype.selectionButtons = function () {
    var $target = $(this),
        $controls = $target.closest('.controls');

    $controls.find('input[type="checkbox"]:not(:checked), input[type="radio"]:not(:checked)')
        .closest('.control__label')
        .removeClass('is-selected');

    if ($target.is(':checked')) {
        $target.closest('.control__label').addClass('is-selected');
    } else {
        $target.closest('.control__label').removeClass('is-selected');
    }
}

/**
 * Initiate Select2
 * @param target
 */
PulsarFormComponent.prototype.initSelect2 = function (target) {
    var $target = target;

    $target.each(function() {
        var $this = $(this),
            config = {};

        function formatOption(data) {
            return $('' + data.text + '');
        }

        if ($this.data('html')) {
            config.templateResult = formatOption;
            config.templateSelection = formatOption;
        }

        $this.select2(config);
        $this.parent().find('.select2-container').removeAttr('style');
    });
}

module.exports = PulsarFormComponent;
<commit_msg>Stop form.color helper throwing an a11y error becuase of an unlabelled input<commit_after>'use strict';

var $ = require('jquery'),
    TimePickerComponent = require('./TimePickerComponent');

require('../libs/pikaday/plugins/pikaday.jquery');
require('../libs/select2/dist/js/select2.min');
require('../libs/spectrum/spectrum');

function PulsarFormComponent(html) {
    this.$html = html;
    this.timePickerComponent = new TimePickerComponent();
}

PulsarFormComponent.prototype.init = function () {
    var component = this;

    // Colour picker
    component.initColourpickers();

    // Date picker
    component.initDatePickers();

    // Choice block
    component.initSelectionButtons();

    // Select2
    component.initSelect2(component.$html.find('.js-select2:not([data-init="false"])'));

    // Time picker
    component.initTimePickers();

    // reinitialise select2 items in a tab when the tab is focused to fix widths
    $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
        var $target = $($(e.target).attr('href')),
            $select2 = $target.find('.js-select2:not([data-init="false"])');

        $.each($select2, function() {
            component.initSelect2($(this));
        });
    });

    // reinitialise select2 items when opening a modal to fix widths
    $('[data-toggle="modal"]').on('click', function (e) {
        var $target = $($(e.target).attr('href')),
            $select2 = $target.find('.js-select2:not([data-init="false"])');

        $target.on('shown.bs.modal', function() {
            $.each($select2, function() {
                component.initSelect2($(this));
            });
        });
    });
}

/**
 * Re-initialise the Form Component Services
 * I've intentionally left out Select2 here, as programmatic
 * control often requires more custom behaviour than simply re-init'ing
 */
PulsarFormComponent.prototype.refresh = function () {
    // Colour picker
    this.initColourpickers();

    // Date picker
    this.initDatePickers();

    // Choice block
    this.initSelectionButtons();

    // Time picker
    this.initTimePickers();
}

/**
 * Initiate a time picker on data-timepicker fields
 */
PulsarFormComponent.prototype.initTimePickers = function () {
    this.timePickerComponent.init(this.$html.find('[data-timepicker=true]'));
}

/**
 * Initiate a date picker on data-datepicker fields using pickaday
 */
PulsarFormComponent.prototype.initDatePickers = function () {
    // Attach basic pikaday to datepicker fields
    this.$html
        .find('[data-datepicker=true]')
        .pikaday({ format: 'DD/MM/YYYY' });
}

/**
 * Initiate colour pickers
 */
PulsarFormComponent.prototype.initColourpickers = function () {
    var component = this,
        pickers = component.$html.find('.js-colorpicker');

    pickers.each(function() {

        var $this = $(this),
            $input = $this.find('.form__control'),
            $pickerInput = $('<input type="hidden" data-colour-picker-input>'),
            disabledAttr = $input.attr('disabled'),
            isDisabled = false,
            existingPicker = $input.next('[data-colour-picker-input]');

        if (typeof disabledAttr !== typeof undefined && disabledAttr !== false) {
            isDisabled = true;
        }

        if (existingPicker.length) {
            // Remove existing colour picker elements,
            // frustratingly invoking the spectrum 'destroy'
            // method does not remove the sp-replacer
            $pickerInput.spectrum('destroy');
            existingPicker.remove();
            $input.siblings('.sp-replacer').remove();
        }

        $pickerInput.insertAfter($input);

        // changing the picker should update the input
        $pickerInput.spectrum({
            color: '#' + $input.val(),
            disabled: isDisabled,
            showInput: false,
            preferredFormat: 'hex',
            replacerClassName: 'btn',
            change: function (color) {
                if (!$input.attr('disabled')) {
                    $input.val(('' + color).substring(1));
                    $input.trigger('change');
                }
            }
        });

        // changing the input should update the picker
        $input.on('change', function () {
            $pickerInput.spectrum('set', '#' + $input.val());
        });
    });
}

/**
 * Update colour pickers within a scope
 * @param $root
 */
PulsarFormComponent.prototype.updateColourPicker = function ($root) {
    const $input = $root.find('.js-colorpicker .form__control');

    $input.each((index, element) => {
        const $input = $(element);
        const $picker = $input.next();

        $picker.spectrum('set', `#${$input.val()}`);
    });
}

/**
 * Initiate Selection Buttons
 */
PulsarFormComponent.prototype.initSelectionButtons = function () {
    const choiceblock = this.$html.find('.choice--block');

    choiceblock
        .find('input[type="checkbox"]:checked, input[type="radio"]:checked')
        .closest('.control__label')
        .addClass('is-selected');

    choiceblock
        .on('change', '.controls input[type="checkbox"], .controls input[type="radio"]', this.selectionButtons);
}

/**
 * Selection Button className logic
 */
PulsarFormComponent.prototype.selectionButtons = function () {
    var $target = $(this),
        $controls = $target.closest('.controls');

    $controls.find('input[type="checkbox"]:not(:checked), input[type="radio"]:not(:checked)')
        .closest('.control__label')
        .removeClass('is-selected');

    if ($target.is(':checked')) {
        $target.closest('.control__label').addClass('is-selected');
    } else {
        $target.closest('.control__label').removeClass('is-selected');
    }
}

/**
 * Initiate Select2
 * @param target
 */
PulsarFormComponent.prototype.initSelect2 = function (target) {
    var $target = target;

    $target.each(function() {
        var $this = $(this),
            config = {};

        function formatOption(data) {
            return $('' + data.text + '');
        }

        if ($this.data('html')) {
            config.templateResult = formatOption;
            config.templateSelection = formatOption;
        }

        $this.select2(config);
        $this.parent().find('.select2-container').removeAttr('style');
    });
}

module.exports = PulsarFormComponent;
<|endoftext|>