<commit_before>(function($) {
  $.fn.autotabs = function(method) {
    var options,
        active_tab_index,
        processing = false;

    var methods = {
      init: function(params) {

        options = $.extend({},
          this.autotabs.defaults,
          params);

        var ul = '
';
        active_tab_index = (options.active_tab == null ?
                             ($.cookie ? $.cookie(options.cookie_name) : 0) :
                             options.active_tab) || 0;

        return this.each(function() {
          var $this = $(this);
          var children = $this.children(options.tab_pane_selector);

          switch (children.length) {
            case 0 :
              return;

            case 1 :
              if (!options.tab_orphans) {
                children.show();
                return;
              }

            default :
              children.each(function(index, element) {
                ul += helpers.generate_tab(index, element);
              });
          }

          ul += '
';

          var tabs = (options.tabs_selector != '') ? $(options.tabs_selector) : $this;
          if (!tabs) { tabs = $this; }
          tabs.prepend(ul);

          $('ul.' + options.tabs_class + ' li > a', tabs).click(function() {
            if (processing) { return false; }
            processing = true;
            helpers.showLoadingIcon(options);

            var link = $(this);

            link.parent().addClass(options.active_class).siblings('li').removeClass(options.active_class);
            $this.children(options.tab_pane_selector).each(function() {
              $(this).slideUp('fast').removeClass(options.active_class);
            });

            $this.children(options.tab_pane_selector).each(function(index) {
              if (link.attr('rel') == this.id) {
                try {
                  helpers.load(this);
                  if ($.cookie) {
                    $.cookie(options.cookie_name, index, { path: options.cookie_path });
                  }

                }
                catch (e) { console.log(e); }
              }
            });

            return false;
          });

          $this.children(options.tab_pane_selector).each(function(index, elt) {
            if (index == active_tab_index) { helpers.load(elt); }
            else { $(elt).slideUp('fast').removeClass(options.active_class); }
          });
        });
      }
    };

    var helpers = {
      generate_tab: function(index, element) {
        var tab = '
' : '>') +
                  '' +
                  ($(element).attr('title') || $(options.tab_label_selector, $(element)).get(0).innerHTML) +
                  '
';
        return tab;
      },

      load: function(pane) {
        var success = helpers.getSuccess(pane.id);
        if ((url = $(pane).attr('rel')) && ($(pane).html().trim() == '' || options.force_refresh)) {
          $(pane).empty();
          $(pane).load(url, function() {
            $(pane).slideDown('fast').addClass(options.active_class);
            if (success && $.isFunction(success)) {
              success.call(pane);
            };
            processing = false;
            helpers.hideLoadingIcon(options);
          });
        }
        else {
          $(pane).slideDown('fast').addClass(options.active_class);
          if (success && $.isFunction(success)) { success.call(pane); }
          processing = false;
          helpers.hideLoadingIcon(options);
        }
      },

      getSuccess: function(id) {
        if (!options.success) { return null; }
        if ($.isFunction(options.success)) {
          return options.success;
        }

        return options.success[id];
      },

      showLoadingIcon: function(options) {
        if (!options.loading_icon) { return; }
        $(options.loading_icon).show();
      },

      hideLoadingIcon: function(options) {
        if (!options.loading_icon) { return; }
        $(options.loading_icon).hide();
      }
    };

    // Method calling logic
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    }
    else if (typeof method === 'object' || !method) {
      return methods.init.apply(this, arguments);
    }
    else {
      $.error('Method ' +  method + ' does not exist in jQuery.autotabs');
    }
  };

  $.fn.autotabs.defaults = {
      tab_pane_selector: "div",
      tab_label_selector: "h3",
      tabs_class: "autotabs",
      tabs_selector: "",
      active_class: "current",
      active_tab: null,
      cookie_name: "active_tab",
      cookie_path: '/',
      force_refresh: false,
      tab_orphans: false,          // display a tab even for a single orphan child element
      loading_icon: '#loading',
      tab_id: function(id) { return '__' + id; }
  };
})(jQuery);
<commit_msg>fixed use of trim<commit_after>(function($) {
  $.fn.autotabs = function(method) {
    var options,
        active_tab_index,
        processing = false;

    var methods = {
      init: function(params) {

        options = $.extend({},
          this.autotabs.defaults,
          params);

        var ul = '
';
        active_tab_index = (options.active_tab == null ?
                             ($.cookie ? $.cookie(options.cookie_name) : 0) :
                             options.active_tab) || 0;

        return this.each(function() {
          var $this = $(this);
          var children = $this.children(options.tab_pane_selector);

          switch (children.length) {
            case 0 :
              return;

            case 1 :
              if (!options.tab_orphans) {
                children.show();
                return;
              }

            default :
              children.each(function(index, element) {
                ul += helpers.generate_tab(index, element);
              });
          }

          ul += '
';

          var tabs = (options.tabs_selector != '') ? $(options.tabs_selector) : $this;
          if (!tabs) { tabs = $this; }
          tabs.prepend(ul);

          $('ul.' + options.tabs_class + ' li > a', tabs).click(function() {
            if (processing) { return false; }
            processing = true;
            helpers.showLoadingIcon(options);

            var link = $(this);

            link.parent().addClass(options.active_class).siblings('li').removeClass(options.active_class);
            $this.children(options.tab_pane_selector).each(function() {
              $(this).slideUp('fast').removeClass(options.active_class);
            });

            $this.children(options.tab_pane_selector).each(function(index) {
              if (link.attr('rel') == this.id) {
                try {
                  helpers.load(this);
                  if ($.cookie) {
                    $.cookie(options.cookie_name, index, { path: options.cookie_path });
                  }

                }
                catch (e) { console.log(e); }
              }
            });

            return false;
          });

          $this.children(options.tab_pane_selector).each(function(index, elt) {
            if (index == active_tab_index) { helpers.load(elt); }
            else { $(elt).slideUp('fast').removeClass(options.active_class); }
          });
        });
      }
    };

    var helpers = {
      generate_tab: function(index, element) {
        var tab = '
' : '>') +
                  '' +
                  ($(element).attr('title') || $(options.tab_label_selector, $(element)).get(0).innerHTML) +
                  '
';
        return tab;
      },

      load: function(pane) {
        var success = helpers.getSuccess(pane.id);
        if ((url = $(pane).attr('rel')) && ($.trim($(pane).html()) == '' || options.force_refresh)) {
          $(pane).empty();
          $(pane).load(url, function() {
            $(pane).slideDown('fast').addClass(options.active_class);
            if (success && $.isFunction(success)) {
              success.call(pane);
            };
            processing = false;
            helpers.hideLoadingIcon(options);
          });
        }
        else {
          $(pane).slideDown('fast').addClass(options.active_class);
          if (success && $.isFunction(success)) { success.call(pane); }
          processing = false;
          helpers.hideLoadingIcon(options);
        }
      },

      getSuccess: function(id) {
        if (!options.success) { return null; }
        if ($.isFunction(options.success)) {
          return options.success;
        }

        return options.success[id];
      },

      showLoadingIcon: function(options) {
        if (!options.loading_icon) { return; }
        $(options.loading_icon).show();
      },

      hideLoadingIcon: function(options) {
        if (!options.loading_icon) { return; }
        $(options.loading_icon).hide();
      }
    };

    // Method calling logic
    if (methods[method]) {
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    }
    else if (typeof method === 'object' || !method) {
      return methods.init.apply(this, arguments);
    }
    else {
      $.error('Method ' +  method + ' does not exist in jQuery.autotabs');
    }
  };

  $.fn.autotabs.defaults = {
      tab_pane_selector: "div",
      tab_label_selector: "h3",
      tabs_class: "autotabs",
      tabs_selector: "",
      active_class: "current",
      active_tab: null,
      cookie_name: "active_tab",
      cookie_path: '/',
      force_refresh: false,
      tab_orphans: false,          // display a tab even for a single orphan child element
      loading_icon: '#loading',
      tab_id: function(id) { return '__' + id; }
  };
})(jQuery);
<|endoftext|>


<commit_before>// jQuery.dynItems, (c) 2012 Juliusz Gonera, MIT License
// https://github.com/jgonera/jquery.dynitems

(function($) {
  "use strict";

  $.fn.dynItems = function(options) {
    var element = this;
    var o = $.extend({
      defaultData: {},
      items: [],
      removeButton: '.remove',
      n: 0
    }, options);
    var n = o.n;

    function addItem(data, callback) {
      if (data instanceof $.Event) {
        data.preventDefault();
        data = {};
      } else if (data instanceof Function) {
        callback = data;
        data = {};
      }
      var templateData = $.extend({}, o.defaultData, data, { n: n });
      var html = o.template(templateData);
      var item = $(html);
      item.appendTo(element);
      element.show();
      item.find(o.removeButton).click(function() {
        item.remove();
        return false;
      });
      ++n;
      if (callback) callback(item);
    }

    $.each(o.items, function(i, data) { addItem(data); });
    element.addItem = addItem;

    return element;
  };
}(jQuery));

<commit_msg>addItem() now returns the item element<commit_after>// jQuery.dynItems, (c) 2012 Juliusz Gonera, MIT License
// https://github.com/jgonera/jquery.dynitems

(function($) {
  "use strict";

  $.fn.dynItems = function(options) {
    var element = this;
    var o = $.extend({
      defaultData: {},
      items: [],
      removeButton: '.remove',
      n: 0
    }, options);
    var n = o.n;

    function addItem(data, callback) {
      if (data instanceof $.Event) {
        data.preventDefault();
        data = {};
      } else if (data instanceof Function) {
        callback = data;
        data = {};
      }

      var templateData = $.extend({}, o.defaultData, data, { n: n });
      var html = o.template(templateData);
      var item = $(html);
      item.appendTo(element);
      element.show();
      item.find(o.removeButton).click(function(e) {
        e.preventDefault();
        item.remove();
      });

      ++n;
      if (callback) callback(item);
      return item;
    }

    $.each(o.items, function(i, data) { addItem(data); });
    element.addItem = addItem;

    return element;
  };
}(jQuery));

<|endoftext|>


<commit_before>/*!
 * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
 * Dual-licensed under the BSD or MIT licenses
 */
;(function($, window, document, undefined)
{
    var hasTouch = 'ontouchstart' in window;

    /**
     * Detect CSS pointer-events property
     * events are normally disabled on the dragging element to avoid conflicts
     * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
     */
    var hasPointerEvents = (function()
    {
        var el    = document.createElement('div'),
            docEl = document.documentElement;
        if (!('pointerEvents' in el.style)) {
            return false;
        }
        el.style.pointerEvents = 'auto';
        el.style.pointerEvents = 'x';
        docEl.appendChild(el);
        var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
        docEl.removeChild(el);
        return !!supports;
    })();

    var eStart  = hasTouch ? 'touchstart'  : 'mousedown',
        eMove   = hasTouch ? 'touchmove'   : 'mousemove',
        eEnd    = hasTouch ? 'touchend'    : 'mouseup';
        eCancel = hasTouch ? 'touchcancel' : 'mouseup';

    var defaults = {
            listNodeName    : 'ol',
            itemNodeName    : 'li',
            rootClass       : 'dd',
            listClass       : 'dd-list',
            itemClass       : 'dd-item',
            dragClass       : 'dd-dragel',
            handleClass     : 'dd-handle',
            collapsedClass  : 'dd-collapsed',
            placeClass      : 'dd-placeholder',
            emptyClass      : 'dd-empty',
            expandBtnHTML   : '<button data-action="expand">Expand></button>',
            collapseBtnHTML : '<button data-action="collapse">Collapse</button>',
            group           : 0,
            maxDepth        : 5,
            threshold       : 20
        };

    function Plugin(element, options)
    {
        this.w  = $(window);
        this.el = $(element);
        this.options = $.extend({}, defaults, options);
        this.init();
    }

    Plugin.prototype = {

        init: function()
        {
            var list = this;

            list.reset();

            list.el.data('nestable-group', this.options.group);

            list.placeEl = $('
');

            $.each(this.el.find(list.options.itemNodeName), function(k, el) {
                list.setParent($(el));
            });

            list.el.on('click', 'button', function(e) {
                if (list.dragEl || (!hasTouch && e.button !== 0)) {
                    return;
                }
                var target = $(e.currentTarget),
                    action = target.data('action'),
                    item   = target.parent(list.options.itemNodeName);
                if (action === 'collapse') {
                    list.collapseItem(item);
                }
                if (action === 'expand') {
                    list.expandItem(item);
                }
            });

            var onStartEvent = function(e)
            {
                if (!$(e.target).hasClass(list.options.handleClass)) {
                    return;
                }
                if (list.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {
                    return;
                }
                e.preventDefault();
                list.dragStart(hasTouch ? e.touches[0] : e);
            };

            var onMoveEvent = function(e)
            {
                if (list.dragEl) {
                    e.preventDefault();
                    list.dragMove(hasTouch ? e.touches[0] : e);
                }
            };

            var onEndEvent = function(e)
            {
                if (list.dragEl) {
                    e.preventDefault();
                    list.dragStop(hasTouch ? e.touches[0] : e);
                }
            };

            if (hasTouch) {
                list.el[0].addEventListener(eStart, onStartEvent, false);
                window.addEventListener(eMove, onMoveEvent, false);
                window.addEventListener(eEnd, onEndEvent, false);
                window.addEventListener(eCancel, onEndEvent, false);
            } else {
                list.el.on(eStart, onStartEvent);
                list.w.on(eMove, onMoveEvent);
                list.w.on(eEnd, onEndEvent);
            }

        },

        serialize: function()
        {
            var data,
                depth = 0,
                list  = this;
                step  = function(level, depth)
                {
                    var array = [ ],
                        items = level.children(list.options.itemNodeName);
                    items.each(function()
                    {
                        var li   = $(this),
                            item = $.extend({}, li.data()),
                            sub  = li.children(list.options.listNodeName);
                        if (sub.length) {
                            item.children = step(sub, depth + 1);
                        }
                        array.push(item);
                    });
                    return array;
                };
            data = step(list.el.find(list.options.listNodeName + ':first'), depth);
            return data;
        },

        serialise: function()
        {
            return this.serialize();
        },

        reset: function()
        {
            this.mouse = {
                offsetX   : 0,
                offsetY   : 0,
                startX    : 0,
                startY    : 0,
                lastX     : 0,
                lastY     : 0,
                nowX      : 0,
                nowY      : 0,
                distX     : 0,
                distY     : 0,
                dirAx     : 0,
                dirX      : 0,
                dirY      : 0,
                lastDirX  : 0,
                lastDirY  : 0,
                distAxX   : 0,
                distAxY   : 0
            };
            this.moving     = false;
            this.dragEl     = null;
            this.dragRootEl = null;
            this.dragDepth  = 0;
            this.hasNewRoot = false;
            this.pointEl    = null;
        },

        expandItem: function(li)
        {
            li.removeClass(this.options.collapsedClass);
            li.children('[data-action="expand"]').hide();
            li.children('[data-action="collapse"]').show();
            li.children(this.options.listNodeName).show();
        },

        collapseItem: function(li)
        {
            var lists = li.children(this.options.listNodeName);
            if (lists.length) {
                li.addClass(this.options.collapsedClass);
                li.children('[data-action="collapse"]').hide();
                li.children('[data-action="expand"]').show();
                li.children(this.options.listNodeName).hide();
            }
        },

        expandAll: function()
        {
            var list = this;
            list.el.find(list.options.itemNodeName).each(function() {
                list.expandItem($(this));
            });
        },

        collapseAll: function()
        {
            var list = this;
            list.el.find(list.options.itemNodeName).each(function() {
                list.collapseItem($(this));
            });
        },

        setParent: function(li)
        {
            if (li.children(this.options.listNodeName).length) {
                li.prepend($(this.options.expandBtnHTML));
                li.prepend($(this.options.collapseBtnHTML));
            }
            li.children('[data-action="expand"]').hide();
        },

        unsetParent: function(li)
        {
            li.removeClass(this.options.collapsedClass);
            li.children('[data-action]').remove();
            li.children(this.options.listNodeName).remove();
        },

        dragStart: function(e)
        {
            var mouse    = this.mouse,
                target   = $(e.target),
                dragItem = target.parents(this.options.itemNodeName + ':first');

            this.placeEl.css({
            //  'width'         : dragItem.width(),
                'height'        : dragItem.height()
            });

            mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
            mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
            mouse.startX = mouse.lastX = e.pageX;
            mouse.startY = mouse.lastY = e.pageY;

            this.dragRootEl = this.el;

            this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
            this.dragEl.css('width', dragItem.width());
            dragItem.replaceWith(this.placeEl).appendTo(this.dragEl);
            $(document.body).append(this.dragEl);
            this.dragEl.css({
                'left' : e.pageX - mouse.offsetX,
                'top'  : e.pageY - mouse.offsetY
            });
            // total depth of dragging item
            var i, depth,
                items = this.dragEl.find(this.options.itemNodeName);
            for (i = 0; i < items.length; i++) {
                depth = $(items[i]).parents(this.options.listNodeName).length;
                if (depth > this.dragDepth) {
                    this.dragDepth = depth;
                }
            }
        },

        dragStop: function(e)
        {
            this.placeEl.replaceWith(this.dragEl.html());
            this.dragEl.remove();
            this.el.trigger('change');
            if (this.hasNewRoot) {
                this.dragRootEl.trigger('change');
            }
            this.reset();
        },

        dragMove: function(e)
        {
            var list, parent, prev, next, depth,
                opt   = this.options,
                mouse = this.mouse;

            this.dragEl.css({
                'left' : e.pageX - mouse.offsetX,
                'top'  : e.pageY - mouse.offsetY
            });

            // mouse position last events
            mouse.lastX = mouse.nowX;
            mouse.lastY = mouse.nowY;
            // mouse position this events
            mouse.nowX  = e.pageX;
            mouse.nowY  = e.pageY;
            // distance mouse moved between events
            mouse.distX = mouse.nowX - mouse.lastX;
            mouse.distY = mouse.nowY - mouse.lastY;
            // direction mouse was moving
            mouse.lastDirX = mouse.dirX;
            mouse.lastDirY = mouse.dirY;
            // direction mouse is now moving (on both axis)
            mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
            mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
            // axis mouse is now moving on
            var newAx   = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;

            // do nothing on first move
            if (!mouse.moving) {
                mouse.dirAx  = newAx;
                mouse.moving = true;
                return;
            }

            // calc distance moved on this axis (and direction)
            if (mouse.dirAx !== newAx) {
                mouse.distAxX = 0;
                mouse.distAxY = 0;
            } else {
                mouse.distAxX += Math.abs(mouse.distX);
                if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
                    mouse.distAxX = 0;
                }
                mouse.distAxY += Math.abs(mouse.distY);
                if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
                    mouse.distAxY = 0;
                }
            }
            mouse.dirAx = newAx;

            /**
             * move horizontal
             */
            if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
                // reset move distance on x-axis for new phase
                mouse.distAxX = 0;
                prev = this.placeEl.prev(opt.itemNodeName);
                // increase horizontal level if previous sibling exists and is not collapsed
                if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
                    // cannot increase level when item above is collapsed
                    list = prev.find(opt.listNodeName + ':last');
                    // check if depth limit has reached
                    depth = this.placeEl.parents(opt.listNodeName).length;
                    if (depth + this.dragDepth <= opt.maxDepth) {
                        // create new sub-level if one doesn't exist
                        if (!list.length) {
                            list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
                            list.append(this.placeEl);
                            prev.append(list);
                            this.setParent(prev);
                        } else {
                            // else append to next level up
                            list = prev.children(opt.listNodeName + ':last');
                            list.append(this.placeEl);
                        }
                    }
                }
                // decrease horizontal level
                if (mouse.distX < 0) {
                    // we can't decrease a level if an item preceeds the current one
                    next = this.placeEl.next(opt.itemNodeName);
                    if (!next.length) {
                        parent = this.placeEl.parent();
                        this.placeEl.parents(opt.itemNodeName + ':first').after(this.placeEl);
                        if (!parent.children().length) {
                            this.unsetParent(parent.parent());
                        }
                    }
                }
            }

            var isEmpty = false;

            // find list item under cursor
            if (!hasPointerEvents) {
                this.dragEl[0].style.visibility = 'hidden';
            }
            this.pointEl = $(document.elementFromPoint(e.pageX - this.w.scrollLeft(), e.pageY - this.w.scrollTop()));
            if (!hasPointerEvents) {
                this.dragEl[0].style.visibility = 'visible';
            }
            if (this.pointEl.hasClass(opt.handleClass)) {
                this.pointEl = this.pointEl.parent(opt.itemNodeName);
            }
            if (this.pointEl.hasClass(opt.emptyClass)) {
                isEmpty = true;
            }
            else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
                return;
            }

            // find parent list of item under cursor
            var pointElRoot = this.pointEl.parents('.' + opt.rootClass + ':first'),
                isNewRoot   = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');

            /**
             * move vertical
             */
            if (!mouse.dirAx || isNewRoot || isEmpty) {
                // check if groups match if dragging over new root
                if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
                    return;
                }
                // check depth limit
                depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
                if (depth > opt.maxDepth) {
                    return;
                }
                var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
                    parent = this.placeEl.parent();
                // if empty create new list to replace empty placeholder
                if (isEmpty) {
                    list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
                    list.append(this.placeEl);
                    this.pointEl.replaceWith(list);
                }
                else if (before) {
                    this.pointEl.before(this.placeEl);
                }
                else {
                    this.pointEl.after(this.placeEl);
                }
                if (!parent.children().length) {
                    this.unsetParent(parent.parent());
                }
                if (!this.dragRootEl.find(opt.itemNodeName).length) {
                    this.dragRootEl.append('
');
                }
                // parent root list has changed
                if (isNewRoot) {
                    this.hasNewRoot = true;
                    this.dragRootEl = pointElRoot;
                }
            }
        }

    };

    $.fn.nestable = function(params)
    {
        var lists  = this,
            retval = this;

        lists.each(function()
        {
            var plugin = $.data(this, 'nestable');
            if (!plugin) {
                $.data(this, 'nestable', new Plugin(this, params));
                $.data(this, 'nestable-id', new Date().getTime());
            } else {
                if (typeof params === 'string' && typeof plugin[params] === 'function') {
                    retval = plugin[params]();
                }
            }
        });

        return retval || lists;
    };

})(jQuery, window, document);<commit_msg>placeEl css<commit_after>/*!
 * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
 * Dual-licensed under the BSD or MIT licenses
 */
;(function($, window, document, undefined)
{
    var hasTouch = 'ontouchstart' in window;

    /**
     * Detect CSS pointer-events property
     * events are normally disabled on the dragging element to avoid conflicts
     * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
     */
    var hasPointerEvents = (function()
    {
        var el    = document.createElement('div'),
            docEl = document.documentElement;
        if (!('pointerEvents' in el.style)) {
            return false;
        }
        el.style.pointerEvents = 'auto';
        el.style.pointerEvents = 'x';
        docEl.appendChild(el);
        var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
        docEl.removeChild(el);
        return !!supports;
    })();

    var eStart  = hasTouch ? 'touchstart'  : 'mousedown',
        eMove   = hasTouch ? 'touchmove'   : 'mousemove',
        eEnd    = hasTouch ? 'touchend'    : 'mouseup';
        eCancel = hasTouch ? 'touchcancel' : 'mouseup';

    var defaults = {
            listNodeName    : 'ol',
            itemNodeName    : 'li',
            rootClass       : 'dd',
            listClass       : 'dd-list',
            itemClass       : 'dd-item',
            dragClass       : 'dd-dragel',
            handleClass     : 'dd-handle',
            collapsedClass  : 'dd-collapsed',
            placeClass      : 'dd-placeholder',
            emptyClass      : 'dd-empty',
            expandBtnHTML   : '<button data-action="expand">Expand></button>',
            collapseBtnHTML : '<button data-action="collapse">Collapse</button>',
            group           : 0,
            maxDepth        : 5,
            threshold       : 20
        };

    function Plugin(element, options)
    {
        this.w  = $(window);
        this.el = $(element);
        this.options = $.extend({}, defaults, options);
        this.init();
    }

    Plugin.prototype = {

        init: function()
        {
            var list = this;

            list.reset();

            list.el.data('nestable-group', this.options.group);

            list.placeEl = $('
');

            $.each(this.el.find(list.options.itemNodeName), function(k, el) {
                list.setParent($(el));
            });

            list.el.on('click', 'button', function(e) {
                if (list.dragEl || (!hasTouch && e.button !== 0)) {
                    return;
                }
                var target = $(e.currentTarget),
                    action = target.data('action'),
                    item   = target.parent(list.options.itemNodeName);
                if (action === 'collapse') {
                    list.collapseItem(item);
                }
                if (action === 'expand') {
                    list.expandItem(item);
                }
            });

            var onStartEvent = function(e)
            {
                if (!$(e.target).hasClass(list.options.handleClass)) {
                    return;
                }
                if (list.dragEl || (!hasTouch && e.button !== 0) || (hasTouch && e.touches.length !== 1)) {
                    return;
                }
                e.preventDefault();
                list.dragStart(hasTouch ? e.touches[0] : e);
            };

            var onMoveEvent = function(e)
            {
                if (list.dragEl) {
                    e.preventDefault();
                    list.dragMove(hasTouch ? e.touches[0] : e);
                }
            };

            var onEndEvent = function(e)
            {
                if (list.dragEl) {
                    e.preventDefault();
                    list.dragStop(hasTouch ? e.touches[0] : e);
                }
            };

            if (hasTouch) {
                list.el[0].addEventListener(eStart, onStartEvent, false);
                window.addEventListener(eMove, onMoveEvent, false);
                window.addEventListener(eEnd, onEndEvent, false);
                window.addEventListener(eCancel, onEndEvent, false);
            } else {
                list.el.on(eStart, onStartEvent);
                list.w.on(eMove, onMoveEvent);
                list.w.on(eEnd, onEndEvent);
            }

        },

        serialize: function()
        {
            var data,
                depth = 0,
                list  = this;
                step  = function(level, depth)
                {
                    var array = [ ],
                        items = level.children(list.options.itemNodeName);
                    items.each(function()
                    {
                        var li   = $(this),
                            item = $.extend({}, li.data()),
                            sub  = li.children(list.options.listNodeName);
                        if (sub.length) {
                            item.children = step(sub, depth + 1);
                        }
                        array.push(item);
                    });
                    return array;
                };
            data = step(list.el.find(list.options.listNodeName + ':first'), depth);
            return data;
        },

        serialise: function()
        {
            return this.serialize();
        },

        reset: function()
        {
            this.mouse = {
                offsetX   : 0,
                offsetY   : 0,
                startX    : 0,
                startY    : 0,
                lastX     : 0,
                lastY     : 0,
                nowX      : 0,
                nowY      : 0,
                distX     : 0,
                distY     : 0,
                dirAx     : 0,
                dirX      : 0,
                dirY      : 0,
                lastDirX  : 0,
                lastDirY  : 0,
                distAxX   : 0,
                distAxY   : 0
            };
            this.moving     = false;
            this.dragEl     = null;
            this.dragRootEl = null;
            this.dragDepth  = 0;
            this.hasNewRoot = false;
            this.pointEl    = null;
        },

        expandItem: function(li)
        {
            li.removeClass(this.options.collapsedClass);
            li.children('[data-action="expand"]').hide();
            li.children('[data-action="collapse"]').show();
            li.children(this.options.listNodeName).show();
        },

        collapseItem: function(li)
        {
            var lists = li.children(this.options.listNodeName);
            if (lists.length) {
                li.addClass(this.options.collapsedClass);
                li.children('[data-action="collapse"]').hide();
                li.children('[data-action="expand"]').show();
                li.children(this.options.listNodeName).hide();
            }
        },

        expandAll: function()
        {
            var list = this;
            list.el.find(list.options.itemNodeName).each(function() {
                list.expandItem($(this));
            });
        },

        collapseAll: function()
        {
            var list = this;
            list.el.find(list.options.itemNodeName).each(function() {
                list.collapseItem($(this));
            });
        },

        setParent: function(li)
        {
            if (li.children(this.options.listNodeName).length) {
                li.prepend($(this.options.expandBtnHTML));
                li.prepend($(this.options.collapseBtnHTML));
            }
            li.children('[data-action="expand"]').hide();
        },

        unsetParent: function(li)
        {
            li.removeClass(this.options.collapsedClass);
            li.children('[data-action]').remove();
            li.children(this.options.listNodeName).remove();
        },

        dragStart: function(e)
        {
            var mouse    = this.mouse,
                target   = $(e.target),
                dragItem = target.parents(this.options.itemNodeName + ':first');

            this.placeEl.css('height', dragItem.height());

            mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
            mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
            mouse.startX = mouse.lastX = e.pageX;
            mouse.startY = mouse.lastY = e.pageY;

            this.dragRootEl = this.el;

            this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
            this.dragEl.css('width', dragItem.width());
            dragItem.replaceWith(this.placeEl).appendTo(this.dragEl);
            $(document.body).append(this.dragEl);
            this.dragEl.css({
                'left' : e.pageX - mouse.offsetX,
                'top'  : e.pageY - mouse.offsetY
            });
            // total depth of dragging item
            var i, depth,
                items = this.dragEl.find(this.options.itemNodeName);
            for (i = 0; i < items.length; i++) {
                depth = $(items[i]).parents(this.options.listNodeName).length;
                if (depth > this.dragDepth) {
                    this.dragDepth = depth;
                }
            }
        },

        dragStop: function(e)
        {
            this.placeEl.replaceWith(this.dragEl.html());
            this.dragEl.remove();
            this.el.trigger('change');
            if (this.hasNewRoot) {
                this.dragRootEl.trigger('change');
            }
            this.reset();
        },

        dragMove: function(e)
        {
            var list, parent, prev, next, depth,
                opt   = this.options,
                mouse = this.mouse;

            this.dragEl.css({
                'left' : e.pageX - mouse.offsetX,
                'top'  : e.pageY - mouse.offsetY
            });

            // mouse position last events
            mouse.lastX = mouse.nowX;
            mouse.lastY = mouse.nowY;
            // mouse position this events
            mouse.nowX  = e.pageX;
            mouse.nowY  = e.pageY;
            // distance mouse moved between events
            mouse.distX = mouse.nowX - mouse.lastX;
            mouse.distY = mouse.nowY - mouse.lastY;
            // direction mouse was moving
            mouse.lastDirX = mouse.dirX;
            mouse.lastDirY = mouse.dirY;
            // direction mouse is now moving (on both axis)
            mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
            mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
            // axis mouse is now moving on
            var newAx   = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;

            // do nothing on first move
            if (!mouse.moving) {
                mouse.dirAx  = newAx;
                mouse.moving = true;
                return;
            }

            // calc distance moved on this axis (and direction)
            if (mouse.dirAx !== newAx) {
                mouse.distAxX = 0;
                mouse.distAxY = 0;
            } else {
                mouse.distAxX += Math.abs(mouse.distX);
                if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
                    mouse.distAxX = 0;
                }
                mouse.distAxY += Math.abs(mouse.distY);
                if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
                    mouse.distAxY = 0;
                }
            }
            mouse.dirAx = newAx;

            /**
             * move horizontal
             */
            if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
                // reset move distance on x-axis for new phase
                mouse.distAxX = 0;
                prev = this.placeEl.prev(opt.itemNodeName);
                // increase horizontal level if previous sibling exists and is not collapsed
                if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
                    // cannot increase level when item above is collapsed
                    list = prev.find(opt.listNodeName + ':last');
                    // check if depth limit has reached
                    depth = this.placeEl.parents(opt.listNodeName).length;
                    if (depth + this.dragDepth <= opt.maxDepth) {
                        // create new sub-level if one doesn't exist
                        if (!list.length) {
                            list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
                            list.append(this.placeEl);
                            prev.append(list);
                            this.setParent(prev);
                        } else {
                            // else append to next level up
                            list = prev.children(opt.listNodeName + ':last');
                            list.append(this.placeEl);
                        }
                    }
                }
                // decrease horizontal level
                if (mouse.distX < 0) {
                    // we can't decrease a level if an item preceeds the current one
                    next = this.placeEl.next(opt.itemNodeName);
                    if (!next.length) {
                        parent = this.placeEl.parent();
                        this.placeEl.parents(opt.itemNodeName + ':first').after(this.placeEl);
                        if (!parent.children().length) {
                            this.unsetParent(parent.parent());
                        }
                    }
                }
            }

            var isEmpty = false;

            // find list item under cursor
            if (!hasPointerEvents) {
                this.dragEl[0].style.visibility = 'hidden';
            }
            this.pointEl = $(document.elementFromPoint(e.pageX - this.w.scrollLeft(), e.pageY - this.w.scrollTop()));
            if (!hasPointerEvents) {
                this.dragEl[0].style.visibility = 'visible';
            }
            if (this.pointEl.hasClass(opt.handleClass)) {
                this.pointEl = this.pointEl.parent(opt.itemNodeName);
            }
            if (this.pointEl.hasClass(opt.emptyClass)) {
                isEmpty = true;
            }
            else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
                return;
            }

            // find parent list of item under cursor
            var pointElRoot = this.pointEl.parents('.' + opt.rootClass + ':first'),
                isNewRoot   = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');

            /**
             * move vertical
             */
            if (!mouse.dirAx || isNewRoot || isEmpty) {
                // check if groups match if dragging over new root
                if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
                    return;
                }
                // check depth limit
                depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
                if (depth > opt.maxDepth) {
                    return;
                }
                var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
                    parent = this.placeEl.parent();
                // if empty create new list to replace empty placeholder
                if (isEmpty) {
                    list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
                    list.append(this.placeEl);
                    this.pointEl.replaceWith(list);
                }
                else if (before) {
                    this.pointEl.before(this.placeEl);
                }
                else {
                    this.pointEl.after(this.placeEl);
                }
                if (!parent.children().length) {
                    this.unsetParent(parent.parent());
                }
                if (!this.dragRootEl.find(opt.itemNodeName).length) {
                    this.dragRootEl.append('
');
                }
                // parent root list has changed
                if (isNewRoot) {
                    this.hasNewRoot = true;
                    this.dragRootEl = pointElRoot;
                }
            }
        }

    };

    $.fn.nestable = function(params)
    {
        var lists  = this,
            retval = this;

        lists.each(function()
        {
            var plugin = $.data(this, 'nestable');
            if (!plugin) {
                $.data(this, 'nestable', new Plugin(this, params));
                $.data(this, 'nestable-id', new Date().getTime());
            } else {
                if (typeof params === 'string' && typeof plugin[params] === 'function') {
                    retval = plugin[params]();
                }
            }
        });

        return retval || lists;
    };

})(jQuery, window, document);<|endoftext|>


<commit_before>'use strict';

angular.module('gc.dialogController', [])
.controller('DialogController', [
  '$scope', '$element', '$attrs', '$location',
  function DialogController($scope, $element, $attrs, $location) {

    // Creates an element and appends it to the body
    $scope.dialog = {};

    $scope.hideDialog = function hideDialog() {
      if ($scope.$root.$$phase) {
        $scope.show = false;
      } else {
        $scope.$apply(function() {
          $scope.show = false;
        });
      }
    };

    $scope.$watch(function dialogPathWatch(){
      return $location.url();
    }, function(newVal, oldVal) {
      if (newVal !== oldVal) {
        $scope.hideDialog();
      }
    });

    // Watch the variable bound to show
    $scope.$watch('show', function dialogShowWatch(isShown) {
      if (isShown) {
        $scope.dialog.show();
      } else {
        $scope.dialog.hide();
      }
    });

    // Cleanup dialog when the view gets torn down
    $scope.$on('$destroy', function dialogDestroy(){
      $scope.dialog.remove();
    });
  }]);
<commit_msg>Fix jshint<commit_after>'use strict';

angular.module('gc.dialogController', [])
.controller('DialogController', [
  '$scope', '$element', '$attrs', '$location',
  function DialogController($scope, $element, $attrs, $location) {

    // Creates an element and appends it to the body
    $scope.dialog = {};

    $scope.hideDialog = function hideDialog() {
      if ($scope.$root.$$phase) {
        $scope.show = false;
      } else {
        $scope.$apply(function() {
          $scope.show = false;
        });
      }
    };

    $scope.$watch(function dialogPathWatch(){
      return $location.url();
    }, function(newVal, oldVal) {
      if (newVal !== oldVal) {
        $scope.hideDialog();
      }
    });

    // Watch the variable bound to show
    $scope.$watch('show', function dialogShowWatch(isShown) {
      if (isShown) {
        $scope.dialog.show();
      } else {
        $scope.dialog.hide();
      }
    });

    // Cleanup dialog when the view gets torn down
    $scope.$on('$destroy', function dialogDestroy(){
      $scope.dialog.remove();
    });
  }

]);
<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Box that can be expanded/collapsed to show/hide contents.
 *
 * @author John Blanco
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // Imports
  var ExpandCollapseButton = require( 'SUN/ExpandCollapseButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Property = require( 'AXON/Property' );

  /**
   * @param {Node} contentNode that will be shown or hidden as the accordion box is expanded/collapsed.
   * @param {Object} [options] Various key-value pairs that control the appearance and behavior.  Some options are
   * specific to this class while some are passed to the superclass.  See the code where the options are set in the
   * early portion of the constructor for details.
   * @constructor
   */
  function AccordionBox( contentNode, options ) {

    options = _.extend( {

      // applied to multiple parts of this UI component
      cursor: 'pointer', // {string} default cursor
      lineWidth: 1,
      cornerRadius: 3,

      // box
      stroke: 'black',
      fill: 'rgb( 238, 238, 238 )',
      minWidth: 0,

      // title
      titleNode: new Text( '' ), // a {Node} with well-defined bounds
      titleAlignX: 'center', // {string} horizontal alignment of the title, 'left'|'center'|'right'
      titleAlignY: 'center', // {string} vertical alignment of the title, relative to expand/collapse button 'top'|'center'
      titleXMargin: 10, // horizontal space between title and left|right edge of box
      titleYMargin: 2, // vertical space between title and top of box
      titleXSpacing: 5, // horizontal space between title and expand/collapse button
      showTitleWhenExpanded: true, // true = title is visible when expanded, false = title is hidden when expanded
      titleBarFill: null, // {Color|string} title bar fill
      titleBarStroke: null, // {Color|string} title bar stroke, used only for the expanded title bar

      // expand/collapse button
      buttonLength: 16, // button is a square, this is the length of one side
      buttonAlign: 'left',  // {string} button alignment, 'left'|'right'
      buttonXMargin: 4, // horizontal space between button and left|right edge of box
      buttonYMargin: 2, // vertical space between button and top edge of box
      expandedProperty: new Property( true ),
      buttonTouchAreaDilatedX: 16,
      buttonTouchAreaDilatedY: 16,
      buttonMouseAreaDilatedX: 0,
      buttonMouseAreaDilatedY: 0,

      // content
      contentAlign: 'center', // {string} horizontal alignment of the content, 'left'|'center'|'right'
      contentXMargin: 15, // horizontal space between content and left/right edges of box
      contentYMargin: 8,  // horizontal space between content and bottom edge of box
      contentXSpacing: 5, // horizontal space between content and button, ignored if showTitleWhenExpanded is true
      contentYSpacing: 8 // vertical space between content and title+button, ignored if showTitleWhenExpanded is false

    }, options );

    // verify string options
    assert && assert( options.buttonAlign === 'left' || options.buttonAlign === 'right' );
    assert && assert( options.contentAlign === 'left' || options.contentAlign === 'right' || options.contentAlign === 'center' );
    assert && assert( options.titleAlignX === 'left' || options.titleAlignX === 'right' || options.titleAlignX === 'center' );
    assert && assert( options.titleAlignY === 'top' || options.titleAlignX === 'center' );

    Node.call( this );

    // @private Expand/collapse button, links to expandedProperty, must be disposed of
    this.expandCollapseButton = new ExpandCollapseButton( options.expandedProperty, {
      sideLength: options.buttonLength,
      cursor: options.cursor
    } );
    this.expandCollapseButton.touchArea = this.expandCollapseButton.localBounds.dilatedXY( options.buttonTouchAreaDilatedX, options.buttonTouchAreaDilatedY );
    this.expandCollapseButton.mouseArea = this.expandCollapseButton.localBounds.dilatedXY( options.buttonMouseAreaDilatedX, options.buttonMouseAreaDilatedY );

    // Compute box dimensions
    var collapsedBoxHeight = Math.max( this.expandCollapseButton.height + ( 2 * options.buttonYMargin ), options.titleNode.height + ( 2 * options.titleYMargin ) );
    assert && assert( options.cornerRadius < collapsedBoxHeight / 2 );
    var boxWidth = Math.max( options.minWidth, this.expandCollapseButton.width + options.titleNode.width + options.buttonXMargin + options.titleXMargin + options.titleXSpacing );
    var expandedBoxHeight;
    if ( options.showTitleWhenExpanded ) {
      // content is below button+title
      boxWidth = Math.max( boxWidth, contentNode.width + ( 2 * options.contentXMargin ) );
      expandedBoxHeight = collapsedBoxHeight + contentNode.height + options.contentYMargin + options.contentYSpacing;
    }
    else {
      // content is next to button
      boxWidth = Math.max( boxWidth, this.expandCollapseButton.width + contentNode.width + options.buttonXMargin + options.contentXMargin + options.contentXSpacing );
      expandedBoxHeight = Math.max( this.expandCollapseButton.height + ( 2 * options.buttonYMargin ), contentNode.height + ( 2 * options.contentYMargin ) );
    }

    // Expanded box
    var boxOptions = { fill: options.fill };
    var expandedBox = new Rectangle( 0, 0, boxWidth, expandedBoxHeight, options.cornerRadius, options.cornerRadius, boxOptions );
    expandedBox.addChild( contentNode );
    this.addChild( expandedBox );

    // Collapsed box
    var collapsedBox = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius, boxOptions );
    this.addChild( collapsedBox );

    // Expanded title bar has rounded top corners, square bottom corners. Clicking it operates like expand/collapse button.
    var expandedTitleBar = null;
    var expandedTitleBarOptions = {
      fill: options.titleBarFill,
      stroke: options.titleBarStroke,
      lineWidth: options.lineWidth, // use same lineWidth as box, for consistent look
      cursor: options.cursor
    };
    if ( options.cornerRadius === 0 || !options.titleBarFill ) {
      // no rounded corners or no title bar visible, so use a Rectangle
      expandedTitleBar = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, expandedTitleBarOptions );
    }
    else {
      // rounded top corners, square bottom corners (start at bottom left and move clockwise)
      expandedTitleBar = new Path( new Shape()
          .moveTo( 0, collapsedBoxHeight )
          .lineTo( 0, options.cornerRadius )
          .arc( options.cornerRadius, options.cornerRadius, options.cornerRadius, Math.PI, 1.5 * Math.PI )
          .lineTo( boxWidth - options.cornerRadius, 0 )
          .arc( boxWidth - options.cornerRadius, options.cornerRadius, options.cornerRadius, 1.5 * Math.PI, 0 )
          .lineTo( boxWidth, collapsedBoxHeight )
          .close(),
        expandedTitleBarOptions );
    }
    expandedBox.addChild( expandedTitleBar );
    expandedTitleBar.addInputListener( {
      down: function() {
        options.expandedProperty.value = false;
      }
    } );

    // Collapsed title bar has corners that match the box. Clicking it operates like expand/collapse button.
    var collapsedTitleBar = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius, {
      fill: options.titleBarFill,
      cursor: options.cursor
    } );
    collapsedBox.addChild( collapsedTitleBar );
    collapsedTitleBar.addInputListener( {
      down: function() {
        options.expandedProperty.value = true;
      }
    } );

    this.addChild( options.titleNode );
    this.addChild( this.expandCollapseButton );

    // box outline, on top of everything else
    var expandedBoxOutline, collapsedBoxOutline;
    if ( options.stroke ) {
      var outlineOptions = { stroke: options.stroke, lineWidth: options.lineWidth };
      expandedBoxOutline = new Rectangle( 0, 0, boxWidth, expandedBoxHeight, options.cornerRadius, options.cornerRadius, outlineOptions );
      collapsedBoxOutline = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius, outlineOptions );
      expandedBox.addChild( expandedBoxOutline );
      collapsedBox.addChild( collapsedBoxOutline );
    }

    // content layout
    contentNode.bottom = expandedBox.bottom - options.contentYMargin;
    var contentSpanLeft = expandedBox.left + options.contentXMargin;
    var contentSpanRight = expandedBox.right - options.contentXMargin;
    if ( !options.showTitleWhenExpanded ) {
      // content will be placed next to button
      if ( options.buttonAlign === 'left' ) {
        contentSpanLeft += this.expandCollapseButton.width + options.contentXSpacing;
      }
      else { // right on right
        contentSpanRight -= this.expandCollapseButton.width + options.contentXSpacing;
      }
    }
    if ( options.contentAlign === 'left' ) {
      contentNode.left = contentSpanLeft;
    }
    else if ( options.contentAlign === 'right' ) {
      contentNode.right = contentSpanRight;
    }
    else { // center
      contentNode.centerX = ( contentSpanLeft + contentSpanRight ) / 2;
    }

    // button horizontal layout
    var titleLeftSpan = expandedBox.left + options.titleXMargin;
    var titleRightSpan = expandedBox.right - options.titleXMargin;
    if ( options.buttonAlign === 'left' ) {
      this.expandCollapseButton.left = expandedBox.left + options.buttonXMargin;
      titleLeftSpan = this.expandCollapseButton.right + options.titleXSpacing;
    }
    else {
      this.expandCollapseButton.right = expandedBox.right - options.buttonXMargin;
      titleRightSpan = this.expandCollapseButton.left - options.titleXSpacing;
    }

    // title horizontal layout
    if ( options.titleAlignX === 'left' ) {
      options.titleNode.left = titleLeftSpan;
    }
    else if ( options.titleAlignX === 'right' ) {
      options.titleNode.right = titleRightSpan;
    }
    else { // center
      options.titleNode.centerX = expandedBox.centerX;
    }

    // button & title vertical layout
    if ( options.titleAlignY === 'top' ) {
      this.expandCollapseButton.top = collapsedBox.top + Math.max( options.buttonYMargin, options.titleYMargin );
      options.titleNode.top = this.expandCollapseButton.top;
    }
    else { // center
      this.expandCollapseButton.centerY = collapsedBox.centerY;
      options.titleNode.centerY = this.expandCollapseButton.centerY;
    }

    // @private expand/collapse the box
    this.expandedPropertyObserver = function( expanded ) {
      expandedBox.visible = expanded;
      collapsedBox.visible = !expanded;
      options.titleNode.visible = ( expanded && options.showTitleWhenExpanded ) || !expanded;
    };
    this.expandedProperty = options.expandedProperty; // @private
    this.expandedProperty.link( this.expandedPropertyObserver ); // must be unlinked in dispose

    this.mutate( _.omit( options, 'cursor' ) );
  }

  return inherit( Node, AccordionBox, {

    // Ensures that this node is eligible for GC.
    dispose: function() {
      this.expandCollapseButton.dispose();
      this.expandCollapseButton = null;
      this.expandedProperty.unlink( this.expandedPropertyObserver );
    }
  } );
} );<commit_msg>Fixing presumptive typo in AccordionBox, see https://github.com/phetsims/sun/issues/159<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Box that can be expanded/collapsed to show/hide contents.
 *
 * @author John Blanco
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // Imports
  var ExpandCollapseButton = require( 'SUN/ExpandCollapseButton' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Property = require( 'AXON/Property' );

  /**
   * @param {Node} contentNode that will be shown or hidden as the accordion box is expanded/collapsed.
   * @param {Object} [options] Various key-value pairs that control the appearance and behavior.  Some options are
   * specific to this class while some are passed to the superclass.  See the code where the options are set in the
   * early portion of the constructor for details.
   * @constructor
   */
  function AccordionBox( contentNode, options ) {

    options = _.extend( {

      // applied to multiple parts of this UI component
      cursor: 'pointer', // {string} default cursor
      lineWidth: 1,
      cornerRadius: 3,

      // box
      stroke: 'black',
      fill: 'rgb( 238, 238, 238 )',
      minWidth: 0,

      // title
      titleNode: new Text( '' ), // a {Node} with well-defined bounds
      titleAlignX: 'center', // {string} horizontal alignment of the title, 'left'|'center'|'right'
      titleAlignY: 'center', // {string} vertical alignment of the title, relative to expand/collapse button 'top'|'center'
      titleXMargin: 10, // horizontal space between title and left|right edge of box
      titleYMargin: 2, // vertical space between title and top of box
      titleXSpacing: 5, // horizontal space between title and expand/collapse button
      showTitleWhenExpanded: true, // true = title is visible when expanded, false = title is hidden when expanded
      titleBarFill: null, // {Color|string} title bar fill
      titleBarStroke: null, // {Color|string} title bar stroke, used only for the expanded title bar

      // expand/collapse button
      buttonLength: 16, // button is a square, this is the length of one side
      buttonAlign: 'left',  // {string} button alignment, 'left'|'right'
      buttonXMargin: 4, // horizontal space between button and left|right edge of box
      buttonYMargin: 2, // vertical space between button and top edge of box
      expandedProperty: new Property( true ),
      buttonTouchAreaDilatedX: 16,
      buttonTouchAreaDilatedY: 16,
      buttonMouseAreaDilatedX: 0,
      buttonMouseAreaDilatedY: 0,

      // content
      contentAlign: 'center', // {string} horizontal alignment of the content, 'left'|'center'|'right'
      contentXMargin: 15, // horizontal space between content and left/right edges of box
      contentYMargin: 8,  // horizontal space between content and bottom edge of box
      contentXSpacing: 5, // horizontal space between content and button, ignored if showTitleWhenExpanded is true
      contentYSpacing: 8 // vertical space between content and title+button, ignored if showTitleWhenExpanded is false

    }, options );

    // verify string options
    assert && assert( options.buttonAlign === 'left' || options.buttonAlign === 'right' );
    assert && assert( options.contentAlign === 'left' || options.contentAlign === 'right' || options.contentAlign === 'center' );
    assert && assert( options.titleAlignX === 'left' || options.titleAlignX === 'right' || options.titleAlignX === 'center' );
    assert && assert( options.titleAlignY === 'top' || options.titleAlignY === 'center' );

    Node.call( this );

    // @private Expand/collapse button, links to expandedProperty, must be disposed of
    this.expandCollapseButton = new ExpandCollapseButton( options.expandedProperty, {
      sideLength: options.buttonLength,
      cursor: options.cursor
    } );
    this.expandCollapseButton.touchArea = this.expandCollapseButton.localBounds.dilatedXY( options.buttonTouchAreaDilatedX, options.buttonTouchAreaDilatedY );
    this.expandCollapseButton.mouseArea = this.expandCollapseButton.localBounds.dilatedXY( options.buttonMouseAreaDilatedX, options.buttonMouseAreaDilatedY );

    // Compute box dimensions
    var collapsedBoxHeight = Math.max( this.expandCollapseButton.height + ( 2 * options.buttonYMargin ), options.titleNode.height + ( 2 * options.titleYMargin ) );
    assert && assert( options.cornerRadius < collapsedBoxHeight / 2 );
    var boxWidth = Math.max( options.minWidth, this.expandCollapseButton.width + options.titleNode.width + options.buttonXMargin + options.titleXMargin + options.titleXSpacing );
    var expandedBoxHeight;
    if ( options.showTitleWhenExpanded ) {
      // content is below button+title
      boxWidth = Math.max( boxWidth, contentNode.width + ( 2 * options.contentXMargin ) );
      expandedBoxHeight = collapsedBoxHeight + contentNode.height + options.contentYMargin + options.contentYSpacing;
    }
    else {
      // content is next to button
      boxWidth = Math.max( boxWidth, this.expandCollapseButton.width + contentNode.width + options.buttonXMargin + options.contentXMargin + options.contentXSpacing );
      expandedBoxHeight = Math.max( this.expandCollapseButton.height + ( 2 * options.buttonYMargin ), contentNode.height + ( 2 * options.contentYMargin ) );
    }

    // Expanded box
    var boxOptions = { fill: options.fill };
    var expandedBox = new Rectangle( 0, 0, boxWidth, expandedBoxHeight, options.cornerRadius, options.cornerRadius, boxOptions );
    expandedBox.addChild( contentNode );
    this.addChild( expandedBox );

    // Collapsed box
    var collapsedBox = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius, boxOptions );
    this.addChild( collapsedBox );

    // Expanded title bar has rounded top corners, square bottom corners. Clicking it operates like expand/collapse button.
    var expandedTitleBar = null;
    var expandedTitleBarOptions = {
      fill: options.titleBarFill,
      stroke: options.titleBarStroke,
      lineWidth: options.lineWidth, // use same lineWidth as box, for consistent look
      cursor: options.cursor
    };
    if ( options.cornerRadius === 0 || !options.titleBarFill ) {
      // no rounded corners or no title bar visible, so use a Rectangle
      expandedTitleBar = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, expandedTitleBarOptions );
    }
    else {
      // rounded top corners, square bottom corners (start at bottom left and move clockwise)
      expandedTitleBar = new Path( new Shape()
          .moveTo( 0, collapsedBoxHeight )
          .lineTo( 0, options.cornerRadius )
          .arc( options.cornerRadius, options.cornerRadius, options.cornerRadius, Math.PI, 1.5 * Math.PI )
          .lineTo( boxWidth - options.cornerRadius, 0 )
          .arc( boxWidth - options.cornerRadius, options.cornerRadius, options.cornerRadius, 1.5 * Math.PI, 0 )
          .lineTo( boxWidth, collapsedBoxHeight )
          .close(),
        expandedTitleBarOptions );
    }
    expandedBox.addChild( expandedTitleBar );
    expandedTitleBar.addInputListener( {
      down: function() {
        options.expandedProperty.value = false;
      }
    } );

    // Collapsed title bar has corners that match the box. Clicking it operates like expand/collapse button.
    var collapsedTitleBar = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius, {
      fill: options.titleBarFill,
      cursor: options.cursor
    } );
    collapsedBox.addChild( collapsedTitleBar );
    collapsedTitleBar.addInputListener( {
      down: function() {
        options.expandedProperty.value = true;
      }
    } );

    this.addChild( options.titleNode );
    this.addChild( this.expandCollapseButton );

    // box outline, on top of everything else
    var expandedBoxOutline, collapsedBoxOutline;
    if ( options.stroke ) {
      var outlineOptions = { stroke: options.stroke, lineWidth: options.lineWidth };
      expandedBoxOutline = new Rectangle( 0, 0, boxWidth, expandedBoxHeight, options.cornerRadius, options.cornerRadius, outlineOptions );
      collapsedBoxOutline = new Rectangle( 0, 0, boxWidth, collapsedBoxHeight, options.cornerRadius, options.cornerRadius, outlineOptions );
      expandedBox.addChild( expandedBoxOutline );
      collapsedBox.addChild( collapsedBoxOutline );
    }

    // content layout
    contentNode.bottom = expandedBox.bottom - options.contentYMargin;
    var contentSpanLeft = expandedBox.left + options.contentXMargin;
    var contentSpanRight = expandedBox.right - options.contentXMargin;
    if ( !options.showTitleWhenExpanded ) {
      // content will be placed next to button
      if ( options.buttonAlign === 'left' ) {
        contentSpanLeft += this.expandCollapseButton.width + options.contentXSpacing;
      }
      else { // right on right
        contentSpanRight -= this.expandCollapseButton.width + options.contentXSpacing;
      }
    }
    if ( options.contentAlign === 'left' ) {
      contentNode.left = contentSpanLeft;
    }
    else if ( options.contentAlign === 'right' ) {
      contentNode.right = contentSpanRight;
    }
    else { // center
      contentNode.centerX = ( contentSpanLeft + contentSpanRight ) / 2;
    }

    // button horizontal layout
    var titleLeftSpan = expandedBox.left + options.titleXMargin;
    var titleRightSpan = expandedBox.right - options.titleXMargin;
    if ( options.buttonAlign === 'left' ) {
      this.expandCollapseButton.left = expandedBox.left + options.buttonXMargin;
      titleLeftSpan = this.expandCollapseButton.right + options.titleXSpacing;
    }
    else {
      this.expandCollapseButton.right = expandedBox.right - options.buttonXMargin;
      titleRightSpan = this.expandCollapseButton.left - options.titleXSpacing;
    }

    // title horizontal layout
    if ( options.titleAlignX === 'left' ) {
      options.titleNode.left = titleLeftSpan;
    }
    else if ( options.titleAlignX === 'right' ) {
      options.titleNode.right = titleRightSpan;
    }
    else { // center
      options.titleNode.centerX = expandedBox.centerX;
    }

    // button & title vertical layout
    if ( options.titleAlignY === 'top' ) {
      this.expandCollapseButton.top = collapsedBox.top + Math.max( options.buttonYMargin, options.titleYMargin );
      options.titleNode.top = this.expandCollapseButton.top;
    }
    else { // center
      this.expandCollapseButton.centerY = collapsedBox.centerY;
      options.titleNode.centerY = this.expandCollapseButton.centerY;
    }

    // @private expand/collapse the box
    this.expandedPropertyObserver = function( expanded ) {
      expandedBox.visible = expanded;
      collapsedBox.visible = !expanded;
      options.titleNode.visible = ( expanded && options.showTitleWhenExpanded ) || !expanded;
    };
    this.expandedProperty = options.expandedProperty; // @private
    this.expandedProperty.link( this.expandedPropertyObserver ); // must be unlinked in dispose

    this.mutate( _.omit( options, 'cursor' ) );
  }

  return inherit( Node, AccordionBox, {

    // Ensures that this node is eligible for GC.
    dispose: function() {
      this.expandCollapseButton.dispose();
      this.expandCollapseButton = null;
      this.expandedProperty.unlink( this.expandedPropertyObserver );
    }
  } );
} );<|endoftext|>


<commit_before>console.log("Well hello there!");

var daysRemaining=(function(){
    var oneDay = 24*60*60*1000;
    var decision = new Date(2017, 6, 25, 23, 59);
    return Math.round(Math.abs((Date.now() - decision.getTime())/(oneDay)));
})();

console.log("days remaining: " + daysRemaining);

$(".toggle-button-question .toggle-select").click(function() {
    $(".toggle-button-question .toggle-select").removeClass("selected");
    $(this).addclass("selected");
});

$(".protest-march-answer-box .toggle-select-protest").click(function() {
    $(".protest-march-answer-box .toggle-select-protest").removeClass("selected");
    $(this).addclass("selected");
});

$("#secret-ballot-preview-message").hide();

var senderName = $(".name-input").val();
var senderEmail = $(".email-input").val();
var emailContent = $("#comment").val(); // two id="comment" on the page

$("#previewEmail").click(function(e) {
    e.preventDefault();
    $("#name").val(senderName);
    $("#email").val(senderEmail);
    $("#comment").val(emailContent);
    $("#secret-ballot-build-message").hide();
    $("#secret-ballot-preview-message").show();
});

var submitForm = function() {
  jQuery.ajax('/api/v1/email/', {
    type: 'POST',
    data: {
      person: 7720,
      name: senderName,
      email: senderEmail,
      body: 'Dear Baleka, stuff.',
      subject: 'Re: Secret Ballot in Vote of No Confidence in Jacob Zuma as President of the Republic',
      gRecaptchaResponse: '2398f49f293fjfj20fj'
    },
    success: function(data) {
      console.info("success", data);
    },
    error: function(jqXHR, textStatus, errorThrown) {
      console.error(jqXHR, textStatus, errorThrown, jqXHR.responseText);
    }
  });
};<commit_msg>not hiding preview yet<commit_after>console.log("Well hello there!");

var daysRemaining=(function(){
    var oneDay = 24*60*60*1000;
    var decision = new Date(2017, 6, 25, 23, 59);
    return Math.round(Math.abs((Date.now() - decision.getTime())/(oneDay)));
})();

console.log("days remaining: " + daysRemaining);

$(".toggle-button-question .toggle-select").click(function() {
    $(".toggle-button-question .toggle-select").removeClass("selected");
    $(this).addclass("selected");
});

$(".protest-march-answer-box .toggle-select-protest").click(function() {
    $(".protest-march-answer-box .toggle-select-protest").removeClass("selected");
    $(this).addclass("selected");
});

// $("#secret-ballot-preview-message").hide();

var senderName = $(".name-input").val();
var senderEmail = $(".email-input").val();
var emailContent = $("#comment").val(); // two id="comment" on the page

$("#previewEmail").click(function(e) {
    e.preventDefault();
    $("#name").val(senderName);
    $("#email").val(senderEmail);
    $("#comment").val(emailContent);
    $("#secret-ballot-build-message").hide();
    $("#secret-ballot-preview-message").show();
});

var submitForm = function() {
  jQuery.ajax('/api/v1/email/', {
    type: 'POST',
    data: {
      person: 7720,
      name: senderName,
      email: senderEmail,
      body: 'Dear Baleka, stuff.',
      subject: 'Re: Secret Ballot in Vote of No Confidence in Jacob Zuma as President of the Republic',
      gRecaptchaResponse: '2398f49f293fjfj20fj'
    },
    success: function(data) {
      console.info("success", data);
    },
    error: function(jqXHR, textStatus, errorThrown) {
      console.error(jqXHR, textStatus, errorThrown, jqXHR.responseText);
    }
  });
};<|endoftext|>


<commit_before>wm.DataServices = (function () {

    'use strict';

    let getUrl = function (url) {
        return new Promise(function (resolve, reject) {
            let req = new XMLHttpRequest({
                mozSystem: true
            });
            req.open('GET', url, true);
            req.onload = function () {
                if (req.status == 200) {
                    resolve(JSON.parse(req.response));
                } else {
                    reject(Error(req.statusText));
                }
            };
            req.onerror = function () {
                reject(Error('network error'));
            };
            req.send();
        });
    };

    let getGeoLocation = function (url) {
        return new Promise(navigator.geolocation.getCurrentPosition.bind(navigator.geolocation));
    };

    let fetchWaterHardness = function () {
        return getGeoLocation().then(position => {
            let url = wm.URI.parse(wm.Config.waterApiUrl);
            let query = {
                latitude: position.coords.latitude,
                longitude: position.coords.longitude
            };
            url.parts.query = wm.URI.serialize(query);
            return getUrl(url.toString());
        });
    };

/*

https://etherpad.mozilla.org/yhltaFmC1r

*/

    let provideOpenData = function () {
        return Promise.all([
            wm.DataServices.fetchWaterHardness(),
            wm.WashingMachineApi.getProgramUuid()
        ]).then(responses => {

            let [waterResponse, programUuidResponse] = responses;

            console.log(waterResponse);
            console.log(programUuidResponse);

            return Promise.all([
                wm.WashingMachineApi.setWaterHardness(programUuidResponse.programUuid, waterResponse.water.hardness),
                wm.WashingMachineApi.setWashingPowder(programUuidResponse.programUuid, {
                    type: wm.WashingMachineApi.washingPowderType.FULL,
                    dosis: 20 // TODO: fetch from WebService - in ml per 1 kg of "laundry"
                }),
                wm.WashingMachineApi.setFabricConditioner(programUuidResponse.programUuid, {
                    dosis: 4 // TODO: fetch from WebService - in ml per 1 kg of "laundry"
                })
            ]);

        }).catch(ex => {
            console.log(ex);
        });
    };

    // public
    return {
        fetchWaterHardness: fetchWaterHardness,
        provideOpenData: provideOpenData
    };

})();
<commit_msg>Update DataServices.js<commit_after>wm.DataServices = (function () {

    'use strict';

    let getUrl = function (url) {
        return new Promise(function (resolve, reject) {
            let req = new XMLHttpRequest({
                mozSystem: true
            });
            req.open('GET', url, true);
            req.onload = function () {
                if (req.status == 200) {
                    resolve(JSON.parse(req.response));
                } else {
                    reject(Error(req.statusText));
                }
            };
            req.onerror = function () {
                reject(Error('network error'));
            };
            req.send();
        });
    };

    let getGeoLocation = function (url) {
        return new Promise(navigator.geolocation.getCurrentPosition.bind(navigator.geolocation));
    };

    let fetchWaterHardness = function () {
        return getGeoLocation().then(position => {
            let url = wm.URI.parse(wm.Config.waterApiUrl);
            let query = {
                latitude: position.coords.latitude,
                longitude: position.coords.longitude
            };
            url.parts.query = wm.URI.serialize(query);
            return getUrl(url.toString());
        });
    };

    let provideOpenData = function () {
        return Promise.all([
            wm.DataServices.fetchWaterHardness(),
            wm.WashingMachineApi.getProgramUuid()
        ]).then(responses => {

            let [waterResponse, programUuidResponse] = responses;

            console.log(waterResponse);
            console.log(programUuidResponse);

            return Promise.all([
                wm.WashingMachineApi.setWaterHardness(programUuidResponse.programUuid, waterResponse.water.hardness),
                wm.WashingMachineApi.setWashingPowder(programUuidResponse.programUuid, {
                    type: wm.WashingMachineApi.washingPowderType.FULL,
                    dosis: 20 // TODO: fetch from WebService - in ml per 1 kg of "laundry"
                }),
                wm.WashingMachineApi.setFabricConditioner(programUuidResponse.programUuid, {
                    dosis: 4 // TODO: fetch from WebService - in ml per 1 kg of "laundry"
                })
            ]);

        }).catch(ex => {
            console.log(ex);
        });
    };

    // public
    return {
        fetchWaterHardness: fetchWaterHardness,
        provideOpenData: provideOpenData
    };

})();
<|endoftext|>


<commit_before>import * as I from 'immutable';

import * as cosets      from '../fpgroups/cosets';
import * as delaney     from './delaney';
import * as properties  from './properties';
import * as delaney2d   from './delaney2d';
import * as delaney3d   from './delaney3d';
import * as fundamental from './fundamental';
import * as covers      from './covers';
import * as periodic    from '../pgraphs/periodic';

import * as util from '../common/util';

import { matrices } from '../arithmetic/types';
const ops = matrices;

let _timers = null;

export function useTimers(timers) {
  _timers = timers;
}

const _remainingIndices = (ds, i) => ds.indices().filter(j => j != i);

const _edgeTranslations = function _edgeTranslations(cov) {
  _timers && _timers.start('_edgeTranslations');

  _timers && _timers.start('_edgeTranslations: fundamental group');
  const fg  = fundamental.fundamentalGroup(cov);
  const n   = fg.nrGenerators;
  _timers && _timers.stop('_edgeTranslations: fundamental group');

  _timers && _timers.start('_edgeTranslations: relator null space');
  const nul = ops.nullSpace(cosets.relatorMatrix(n, fg.relators).toJS());
  _timers && _timers.stop('_edgeTranslations: relator null space');

  _timers && _timers.start('_edgeTranslations: assign translations to edges');
  const result = fg.edge2word.map(function(a) {
    return a.map(function(b) {
      const v = cosets.relatorAsVector(b, n).toJS();
      return ops.times(v, nul);
    });
  });
  _timers && _timers.stop('_edgeTranslations: assign translations to edges');

  _timers && _timers.stop('_edgeTranslations');

  return result;
};

const _cornerShifts = function _cornerShifts(cov, e2t) {
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);

  return I.Map().withMutations(function(m) {
    cov.indices().forEach(function(i) {
      const idcs = _remainingIndices(cov, i);

      properties.traversal(cov, idcs, cov.elements()).forEach(function(e) {
        const Dk = e[0];
        const k  = e[1];
        const D  = e[2];

        if (k == properties.traversal.root)
          m.setIn([D, i], zero);
        else
          m.setIn([D, i],
                  ops.minus(m.getIn([Dk, i]), e2t.getIn([Dk, k]) || zero));
      });
    });
  });
};

export const skeleton = cov => {
  useTimers(util.timers());

  _timers && _timers.start('skeleton');

  _timers && _timers.start('skeleton: preparations');
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);
  const chambers = cov.elements();
  const idcs0 = _remainingIndices(cov, 0);
  const nodeReps = properties.orbitReps(cov, idcs0, chambers);
  const node2chamber = I.Map(I.Range().zip(nodeReps));
  const chamber2node = I.Map(
    nodeReps
      .zip(I.Range())
      .flatMap(p => properties.orbit(cov, idcs0, p[0]).zip(I.Repeat(p[1]))));
  _timers && _timers.stop('skeleton: preparations');

  _timers && _timers.start('skeleton: edge translations');
  const e2t = _edgeTranslations(cov);
  _timers && _timers.stop('skeleton: edge translations');

  _timers && _timers.start('skeleton: corner shifts');
  const c2s = _cornerShifts(cov, e2t);
  _timers && _timers.stop('skeleton: corner shifts');

  _timers && _timers.start('skeleton: edges');
  const edges = properties.orbitReps(cov, _remainingIndices(cov, 1), chambers)
    .map(function(D) {
      const E = cov.s(0, D);
      const v = chamber2node.get(D);
      const w = chamber2node.get(E);
      const t = e2t.getIn([D, 0]) || zero;
      const sD = c2s.getIn([D, 0]);
      const sE = c2s.getIn([E, 0]);
      const s = ops.minus(ops.plus(t, sE), sD);

      return [v, w, s];
    });
  _timers && _timers.stop('skeleton: edges');

  _timers && _timers.start('skeleton: make graph');
  const graph = periodic.make(edges);
  _timers && _timers.stop('skeleton: make graph');

  _timers && _timers.stop('skeleton');

  console.log(`Skeleton details:`);
  console.log(`${JSON.stringify(_timers.current(), null, 2)}`);

  return {
    graph,
    node2chamber: node2chamber.toJS(),
    chamber2node: chamber2node.toJS(),
    edgeTranslations: e2t.toJS(),
    cornerShifts: c2s.toJS()
  };
};

const chamberPositions = (cov, skel, pos) => {
  const dim = delaney.dim(cov);
  let result = {};

  cov.elements().forEach(function(D) {
    const p = pos[skel.chamber2node[D]];
    const t = skel.cornerShifts[D][0];
    result[D] = [ops.plus(p, t)];
  });

  I.Range(1, dim+1).forEach(function(i) {
    const idcs = I.Range(0, i);
    properties.orbits(cov, idcs, cov.elements()).forEach(function(orb) {
      let s = ops.vector(dim);
      orb.forEach(function(E) {
        const p = result[E][0];
        const t = skel.cornerShifts[E][i];
        s = ops.plus(s, ops.minus(p, t));
      });
      s = ops.times(ops.div(1, orb.size), s);
      orb.forEach(function(E) {
        const t = skel.cornerShifts[E][i];
        result[E].push(ops.plus(s, t));
      });
   });
  });

  return result;
};

const chamberBasis = (pos, D) => {
  const t = pos[D];
  return ops.cleanup(t.slice(1).map(v => ops.minus(v, t[0])));
};

export const symmetries = (ds, cov, pos) => {
  const D0 = cov.elements()
    .find(D => ops.ne(ops.determinant(chamberBasis(pos, D)), 0));
  const A = ops.inverse(chamberBasis(pos, D0));

  const phi = properties.morphism(cov, 1, ds, 1);
  const E0 = phi.get(D0);

  return I.List(cov.elements()).toJS()
    .filter(D => phi.get(D) == E0)
    .map(D => ops.times(A, chamberBasis(pos, D)));
};

export const makeCover = ds =>
  delaney.dim(ds) == 3 ?
  delaney3d.pseudoToroidalCover(ds) :
  delaney2d.toroidalCover(ds);

const interpolate = (f, v, w) => ops.plus(w, ops.times(f, ops.minus(v, w)));

const tileSurface3D = (corners, faces) => {
  const pos = corners.map(p => interpolate(0.8, p[0], p[3]));

  return { pos, faces };
};

const tileSurface2D = (corners, faces) => {
  const pos = I.List(corners).flatMap(p => [p[0].concat(0), p[0].concat(0.1)]);

  const f = faces[0].map(i => 2 * i);

  faces = [f, f.map(x => x + 1).reverse()]
    .concat(f.map((x, i) => {
      const y = f[(i + 1) % f.length];
      return [y, x, x + 1, y + 1];
    }));

  return { pos: pos.toJS(), faces };
};

const determinant = M => {
  if (M.length == 2)
    return M[0][0] * M[1][1] - M[0][1] * M[1][0];
  else if (M.length == 3)
    return (+ M[0][0] * M[1][1] * M[2][2]
            + M[0][1] * M[1][2] * M[2][0]
            + M[0][2] * M[1][0] * M[2][1]
            - M[0][2] * M[1][1] * M[2][0]
            - M[0][1] * M[1][0] * M[2][2]
            - M[0][0] * M[1][2] * M[2][1]);
  else
    return ops.determinant(ops.cleanup(M));
};

const adjustedOrientation = (cov, pos) => {
  const bas = D => chamberBasis(pos, D);
  const D0 = cov.elements().find(D => ops.ne(determinant(bas(D)), 0));
  const sgn = ops.sgn(determinant(bas(D0)));

  const ori = properties.partialOrientation(cov).toJS();
  if (sgn * ori[D0] < 0) {
    for (const D of cov.elements())
      ori[D] = -ori[D];
  }

  return ori;
};

const tileSurface = (cov, pos, ori, elms, idcs) => {
  const cOrbs = properties.orbits(cov, idcs.slice(1), elms);
  const cPos = cOrbs.map(orb => pos[orb.first()]);
  const cIdcs = I.Map(cOrbs.flatMap((orb, i) => orb.map(D => [D, i])));

  const faces = properties.orbits(cov, [0, 1], elms)
    .map(orb => ori[orb.first()] > 0 ? orb.reverse() : orb)
    .map(orb => orb.filter((D, i) => i % 2 == 0).map(D => cIdcs.get(D)));

  if (delaney.dim(cov) == 3)
    return tileSurface3D(cPos.toJS(), faces.toJS());
  else
    return tileSurface2D(cPos.toJS(), faces.toJS());
};

const affineSymmetry = (D0, D1, pos) => {
  const bas = D => chamberBasis(pos, D);
  const linear = ops.cleanup(ops.times(ops.inverse(bas(D0)), bas(D1)));
  const shift = ops.minus(pos[D1][0], ops.times(pos[D0][0], linear));

  return linear.map(r => r.concat(0)).concat([shift.concat([1])]);
};

export const tileSurfaces = (ds, cov, skel, vertexPos, basis) => {
  const dim = delaney.dim(cov);
  const chamberPos = chamberPositions(cov, skel, vertexPos);

  const pos = {};
  for (const D of cov.elements())
    pos[D] = chamberPos[D].map(p => ops.times(p, basis));

  const phi = properties.morphism(cov, 1, ds, 1);
  const bas = D => chamberBasis(pos, D);
  const ori = adjustedOrientation(cov, pos);
  const idcs = I.Range(0, delaney.dim(cov)).toArray();
  const tileOrbits = properties.orbits(cov, idcs).toArray();

  const templates = [];
  const tileOrbitReps = [];
  const dsChamberToTemplateIndex = {};
  const tiles = [];

  for (const elms of tileOrbits) {
    const D0 = elms.find(D => ops.ne(determinant(bas(D)), 0));
    const E0 = phi.get(D0);

    let templateIndex = dsChamberToTemplateIndex[E0];
    let symmetry = ops.identityMatrix(dim + 1);

    if (templateIndex == null) {
      templateIndex = templates.length;

      for (const E of properties.orbit(ds, idcs, E0))
        dsChamberToTemplateIndex[E] = templateIndex;

      templates.push(tileSurface(cov, pos, ori, elms, idcs));
      tileOrbitReps.push(D0);
    }
    else {
      const D0 = tileOrbitReps[templateIndex];
      const D1 = elms.find(D => phi.get(D) == phi.get(D0));
      symmetry = affineSymmetry(D0, D1, pos);
    }

    const center = pos[elms.first()][dim];
    tiles.push({ templateIndex, symmetry, center });
  }

  return { templates, tiles };
};
<commit_msg>refactor the skeleton computation<commit_after>import * as I from 'immutable';

import * as cosets      from '../fpgroups/cosets';
import * as delaney     from './delaney';
import * as properties  from './properties';
import * as delaney2d   from './delaney2d';
import * as delaney3d   from './delaney3d';
import * as fundamental from './fundamental';
import * as covers      from './covers';
import * as periodic    from '../pgraphs/periodic';

import * as util from '../common/util';

import { matrices } from '../arithmetic/types';
const ops = matrices;

let _timers = null;

export function useTimers(timers) {
  _timers = timers;
}

const _remainingIndices = (ds, i) => ds.indices().filter(j => j != i);

const _edgeTranslations = function _edgeTranslations(cov) {
  _timers && _timers.start('_edgeTranslations');

  _timers && _timers.start('_edgeTranslations: fundamental group');
  const fg  = fundamental.fundamentalGroup(cov);
  const n   = fg.nrGenerators;
  _timers && _timers.stop('_edgeTranslations: fundamental group');

  _timers && _timers.start('_edgeTranslations: relator null space');
  const nul = ops.nullSpace(cosets.relatorMatrix(n, fg.relators).toJS());
  _timers && _timers.stop('_edgeTranslations: relator null space');

  _timers && _timers.start('_edgeTranslations: assign translations to edges');
  const result = fg.edge2word.map(function(a) {
    return a.map(function(b) {
      const v = cosets.relatorAsVector(b, n).toJS();
      return ops.times(v, nul);
    });
  });
  _timers && _timers.stop('_edgeTranslations: assign translations to edges');

  _timers && _timers.stop('_edgeTranslations');

  return result;
};

const _cornerShifts = function _cornerShifts(cov, e2t) {
  const dim = delaney.dim(cov);
  const zero = ops.vector(dim);

  return I.Map().withMutations(function(m) {
    cov.indices().forEach(function(i) {
      const idcs = _remainingIndices(cov, i);

      properties.traversal(cov, idcs, cov.elements()).forEach(function(e) {
        const Dk = e[0];
        const k  = e[1];
        const D  = e[2];

        if (k == properties.traversal.root)
          m.setIn([D, i], zero);
        else
          m.setIn([D, i],
                  ops.minus(m.getIn([Dk, i]), e2t.getIn([Dk, k]) || zero));
      });
    });
  });
};

export const skeleton = cov => {
  useTimers(util.timers());

  _timers && _timers.start('skeleton');

  _timers && _timers.start('skeleton: edge translations');
  const e2t = _edgeTranslations(cov);
  _timers && _timers.stop('skeleton: edge translations');

  _timers && _timers.start('skeleton: corner shifts');
  const c2s = _cornerShifts(cov, e2t);
  _timers && _timers.stop('skeleton: corner shifts');

  _timers && _timers.start('skeleton: chamber2node');
  const chamber2node = {};
  let node = 1;
  for (const orb of properties.orbits(cov, _remainingIndices(cov, 0))) {
    for (const D of orb)
      chamber2node[D] = node;
    node += 1;
  }
  _timers && _timers.stop('skeleton: chamber2node');

  _timers && _timers.start('skeleton: edges');
  const zero = ops.vector(delaney.dim(cov));
  const edges = properties.orbitReps(cov, _remainingIndices(cov, 1))
    .map(function(D) {
      const E = cov.s(0, D);
      const v = chamber2node[D];
      const w = chamber2node[E];
      const t = e2t.getIn([D, 0]) || zero;
      const sD = c2s.getIn([D, 0]);
      const sE = c2s.getIn([E, 0]);
      const s = ops.minus(ops.plus(t, sE), sD);

      return [v, w, s];
    });
  _timers && _timers.stop('skeleton: edges');

  _timers && _timers.start('skeleton: make graph');
  const graph = periodic.make(edges);
  _timers && _timers.stop('skeleton: make graph');

  _timers && _timers.stop('skeleton');

  console.log(`Skeleton details:`);
  console.log(`${JSON.stringify(_timers.current(), null, 2)}`);

  return {
    graph,
    chamber2node: chamber2node,
    edgeTranslations: e2t.toJS(),
    cornerShifts: c2s.toJS()
  };
};

const chamberPositions = (cov, skel, pos) => {
  const dim = delaney.dim(cov);
  let result = {};

  cov.elements().forEach(function(D) {
    const p = pos[skel.chamber2node[D]];
    const t = skel.cornerShifts[D][0];
    result[D] = [ops.plus(p, t)];
  });

  I.Range(1, dim+1).forEach(function(i) {
    const idcs = I.Range(0, i);
    properties.orbits(cov, idcs, cov.elements()).forEach(function(orb) {
      let s = ops.vector(dim);
      orb.forEach(function(E) {
        const p = result[E][0];
        const t = skel.cornerShifts[E][i];
        s = ops.plus(s, ops.minus(p, t));
      });
      s = ops.times(ops.div(1, orb.size), s);
      orb.forEach(function(E) {
        const t = skel.cornerShifts[E][i];
        result[E].push(ops.plus(s, t));
      });
   });
  });

  return result;
};

const chamberBasis = (pos, D) => {
  const t = pos[D];
  return ops.cleanup(t.slice(1).map(v => ops.minus(v, t[0])));
};

export const symmetries = (ds, cov, pos) => {
  const D0 = cov.elements()
    .find(D => ops.ne(ops.determinant(chamberBasis(pos, D)), 0));
  const A = ops.inverse(chamberBasis(pos, D0));

  const phi = properties.morphism(cov, 1, ds, 1);
  const E0 = phi.get(D0);

  return I.List(cov.elements()).toJS()
    .filter(D => phi.get(D) == E0)
    .map(D => ops.times(A, chamberBasis(pos, D)));
};

export const makeCover = ds =>
  delaney.dim(ds) == 3 ?
  delaney3d.pseudoToroidalCover(ds) :
  delaney2d.toroidalCover(ds);

const interpolate = (f, v, w) => ops.plus(w, ops.times(f, ops.minus(v, w)));

const tileSurface3D = (corners, faces) => {
  const pos = corners.map(p => interpolate(0.8, p[0], p[3]));

  return { pos, faces };
};

const tileSurface2D = (corners, faces) => {
  const pos = I.List(corners).flatMap(p => [p[0].concat(0), p[0].concat(0.1)]);

  const f = faces[0].map(i => 2 * i);

  faces = [f, f.map(x => x + 1).reverse()]
    .concat(f.map((x, i) => {
      const y = f[(i + 1) % f.length];
      return [y, x, x + 1, y + 1];
    }));

  return { pos: pos.toJS(), faces };
};

const determinant = M => {
  if (M.length == 2)
    return M[0][0] * M[1][1] - M[0][1] * M[1][0];
  else if (M.length == 3)
    return (+ M[0][0] * M[1][1] * M[2][2]
            + M[0][1] * M[1][2] * M[2][0]
            + M[0][2] * M[1][0] * M[2][1]
            - M[0][2] * M[1][1] * M[2][0]
            - M[0][1] * M[1][0] * M[2][2]
            - M[0][0] * M[1][2] * M[2][1]);
  else
    return ops.determinant(ops.cleanup(M));
};

const adjustedOrientation = (cov, pos) => {
  const bas = D => chamberBasis(pos, D);
  const D0 = cov.elements().find(D => ops.ne(determinant(bas(D)), 0));
  const sgn = ops.sgn(determinant(bas(D0)));

  const ori = properties.partialOrientation(cov).toJS();
  if (sgn * ori[D0] < 0) {
    for (const D of cov.elements())
      ori[D] = -ori[D];
  }

  return ori;
};

const tileSurface = (cov, pos, ori, elms, idcs) => {
  const cOrbs = properties.orbits(cov, idcs.slice(1), elms);
  const cPos = cOrbs.map(orb => pos[orb.first()]);
  const cIdcs = I.Map(cOrbs.flatMap((orb, i) => orb.map(D => [D, i])));

  const faces = properties.orbits(cov, [0, 1], elms)
    .map(orb => ori[orb.first()] > 0 ? orb.reverse() : orb)
    .map(orb => orb.filter((D, i) => i % 2 == 0).map(D => cIdcs.get(D)));

  if (delaney.dim(cov) == 3)
    return tileSurface3D(cPos.toJS(), faces.toJS());
  else
    return tileSurface2D(cPos.toJS(), faces.toJS());
};

const affineSymmetry = (D0, D1, pos) => {
  const bas = D => chamberBasis(pos, D);
  const linear = ops.cleanup(ops.times(ops.inverse(bas(D0)), bas(D1)));
  const shift = ops.minus(pos[D1][0], ops.times(pos[D0][0], linear));

  return linear.map(r => r.concat(0)).concat([shift.concat([1])]);
};

export const tileSurfaces = (ds, cov, skel, vertexPos, basis) => {
  const dim = delaney.dim(cov);
  const chamberPos = chamberPositions(cov, skel, vertexPos);

  const pos = {};
  for (const D of cov.elements())
    pos[D] = chamberPos[D].map(p => ops.times(p, basis));

  const phi = properties.morphism(cov, 1, ds, 1);
  const bas = D => chamberBasis(pos, D);
  const ori = adjustedOrientation(cov, pos);
  const idcs = I.Range(0, delaney.dim(cov)).toArray();
  const tileOrbits = properties.orbits(cov, idcs).toArray();

  const templates = [];
  const tileOrbitReps = [];
  const dsChamberToTemplateIndex = {};
  const tiles = [];

  for (const elms of tileOrbits) {
    const D0 = elms.find(D => ops.ne(determinant(bas(D)), 0));
    const E0 = phi.get(D0);

    let templateIndex = dsChamberToTemplateIndex[E0];
    let symmetry = ops.identityMatrix(dim + 1);

    if (templateIndex == null) {
      templateIndex = templates.length;

      for (const E of properties.orbit(ds, idcs, E0))
        dsChamberToTemplateIndex[E] = templateIndex;

      templates.push(tileSurface(cov, pos, ori, elms, idcs));
      tileOrbitReps.push(D0);
    }
    else {
      const D0 = tileOrbitReps[templateIndex];
      const D1 = elms.find(D => phi.get(D) == phi.get(D0));
      symmetry = affineSymmetry(D0, D1, pos);
    }

    const center = pos[elms.first()][dim];
    tiles.push({ templateIndex, symmetry, center });
  }

  return { templates, tiles };
};
<|endoftext|>