<commit_before>(function (window) {
    'use strict';

    function Bullet(id, x, y, resources) {

        this.resources = resources;

        this.id = id;

        this.moveLoop = null;

        // Positions
        this.x = x;
        this.y = y;

        // Taille en pixels d'une frame d'animation
        this.frameSize = {
            width: 6,
            height: 6
        };
    }

    Bullet.prototype.clearCurrentMove = function () {
        if (this.moveLoop) {
            clearTimeout(this.moveLoop);
        }
    };

    Bullet.prototype.move = function (path) {

        var sequence = Promise.resolve();

        path.forEach(function (coords) {
            sequence = sequence.then(function () {
                return new Promise(function (resolve) {
                    this.moveLoop = setTimeout(function () {
                        this.x = coords.x;
                        this.y = coords.y;
                        resolve(this);
                    }.bind(this), 5);
                }.bind(this));
            }.bind(this));
        }.bind(this));

        return sequence;
    };

    Bullet.prototype.paint = function (context) {

        context.drawImage(this.resources.images.bullet,
            0, 0,
            this.frameSize.width, this.frameSize.height,
            // centrage de l'image par rapport à la position
            this.x - (this.frameSize.width / 2), this.y - this.frameSize.height + (this.frameSize.height / 2),
            this.frameSize.width, this.frameSize.height
        );
    };

    Bullet.prototype.hitbox = function () {
        return new RectangleRightHitBox(this, this.frameSize.width, this.frameSize.height);
    };

    window.Bullet = Bullet;

})(window);
<commit_msg>refactor(bullet): externalisation de la vitesse<commit_after>(function (window) {
    'use strict';

    function Bullet(id, x, y, resources) {

        this.resources = resources;

        this.id = id;

        this.moveLoop = null;

        // Positions
        this.x = x;
        this.y = y;

        // Taille en pixels d'une frame d'animation
        this.frameSize = {
            width: 6,
            height: 6
        };

        // vitesse d'une bullet
        this.speed = 5;
    }

    Bullet.prototype.clearCurrentMove = function () {
        if (this.moveLoop) {
            clearTimeout(this.moveLoop);
        }
    };

    Bullet.prototype.move = function (path) {

        var sequence = Promise.resolve();

        path.forEach(function (coords) {
            sequence = sequence.then(function () {
                return new Promise(function (resolve) {
                    this.moveLoop = setTimeout(function () {
                        this.x = coords.x;
                        this.y = coords.y;
                        resolve(this);
                    }.bind(this), this.speed);
                }.bind(this));
            }.bind(this));
        }.bind(this));

        return sequence;
    };

    Bullet.prototype.paint = function (context) {

        context.drawImage(this.resources.images.bullet,
            0, 0,
            this.frameSize.width, this.frameSize.height,
            // centrage de l'image par rapport à la position
            this.x - (this.frameSize.width / 2), this.y - this.frameSize.height + (this.frameSize.height / 2),
            this.frameSize.width, this.frameSize.height
        );
    };

    Bullet.prototype.hitbox = function () {
        return new RectangleRightHitBox(this, this.frameSize.width, this.frameSize.height);
    };

    window.Bullet = Bullet;

})(window);
<|endoftext|>


<commit_before>define(function(require, exports, module) {

  function Editor(options) {
    this.init(options);
  }

  Editor.prototype.init = function(options) {
    options = options || {};
    if (options.element) {
      this.element = options.element;
    }
    options.tools = options.tools || [
      'bold', 'italic', 'separator',
      'quote', 'unordered-list', 'ordered-list', 'separator',
      'link', 'image', 'separator',
      'undo', 'redo', 'separator',
      'info', 'expand'
    ];
    options.status = options.status || ['lines', 'words', 'cursor'];
    this.options = options;
  };

  Editor.prototype.render = function(el) {
    if (!el) {
      el = this.element || document.getElementsByTagName('textarea')[0];
    }
    this.element = el;

    var self = this;

    var keyMaps;

    if (/Mac/.test(navigator.platform)) {
      keyMaps = {
        'Cmd-B': function(cm) {
          self.action('bold', cm);
        },
        'Cmd-I': function(cm) {
          self.action('italic', cm);
        },
        'Cmd-L': function(cm) {
          self.action('link', cm);
        },
        'Shift-Cmd-O': function(cm) {
          self.action('ordered-list', cm);
        },
        'Shift-Cmd-U': function(cm) {
          self.action('unordered-list', cm);
        },
        'Shift-Cmd-I': function(cm) {
          self.action('image', cm);
        }
      };

    } else {
      keyMaps = {
        'Ctrl-B': function(cm) {
          self.action('bold', cm);
        },
        'Ctrl-I': function(cm) {
          self.action('italic', cm);
        },
        'Ctrl-L': function(cm) {
          self.action('link', cm);
        },
        'Shift-Ctrl-O': function(cm) {
          self.action('ordered-list', cm);
        },
        'Shift-Ctrl-U': function(cm) {
          self.action('unordered-list', cm);
        },
        'Shift-Ctrl-I': function(cm) {
          self.action('image', cm);
        }
      };
    }

    var editor = CodeMirror.fromTextArea(el, {
      mode: 'gfm',
      theme: 'paper',
      indentWithTabs: true,
      lineNumbers: false,
      extraKeys: keyMaps
    });
    this.editor = editor;

    var bar = this.createToolbar();
    this.createStatusbar();

    // ie < 9 sucks
    if (!bar.classList || !bar.querySelector) return;

    editor.on('cursorActivity', function() {
      var icons = bar.getElementsByTagName('span');

      for (var i = 0; i < icons.length; i++) {
        var el = icons[i];
        el.classList.remove('active');
      }

      var stat = getState(editor);
      for (var key in stat) {
        if (stat[key]) {
          el = document.querySelector('.icon-' + fixIcon(key));
          el.classList.add('active');
        }
      }
    });
  };

  Editor.prototype.createToolbar = function(tools) {
    tools = tools || this.options.tools;

    if (!tools || tools.length === 0) return;

    var bar = document.createElement('div');
    bar.className = 'editor-toolbar';

    var el, self = this;
    for (var i = 0; i < tools.length; i++) {
      name = tools[i];
      (function(name) {
        el = createIcon(name);
        // bind events
        el.onclick = function() {
          return self.action(name);
        };
        bar.appendChild(el);
      })(tools[i]);
    }
    var editorElement = this.editor.getWrapperElement();
    editorElement.parentNode.insertBefore(bar, editorElement);
    return bar;
  };

  Editor.prototype.createStatusbar = function(status) {
    status = status || this.options.status;

    if (!status || status.length === 0) return;

    var bar = document.createElement('div');
    bar.className = 'editor-statusbar';

    var pos, editor = this.editor;
    for (var i = 0; i < status.length; i++) {
      (function(name) {
        var el = document.createElement('span');
        el.className = name;
        if (name === 'words') {
          el.innerHTML = '0';
          editor.on('update', function() {
            el.innerHTML = editor.getValue().length;
          });
        } else if (name === 'lines') {
          el.innerHTML = '0';
          editor.on('update', function() {
            el.innerHTML = editor.lineCount();
          });
        } else if (name === 'cursor') {
          el.innerHTML = '0:0';
          editor.on('cursorActivity', function() {
            pos = editor.getCursor();
            el.innerHTML = pos.line + ':' + pos.ch;
          });
        }
        bar.appendChild(el);
      })(status[i]);
    }
    var editorElement = this.editor.getWrapperElement();
    editorElement.parentNode.insertBefore(bar, editorElement.nextSibling);
    return bar;
  };

  Editor.prototype.action = function(name, ed) {
    ed = ed || this.editor;
    if (!ed) return;
    var stat = getState(ed);

    var replaceSelection = function(start, end) {
      var pos, text;
      if (stat[name]) {
        pos = ed.getCursor('start');
        text = ed.getLine(pos.line);
        start = text.slice(0, pos.ch);
        end = text.slice(pos.ch);
        if (name === 'bold') {
          start = start.replace(/^(.*)?(\*|\_){2}(\S+.*)?$/, '$1$3');
          end = end.replace(/^(.*\S+)?(\*|\_){2}(\s+.*)?$/, '$1$3');
        } else if (name === 'italic') {
          start = start.replace(/^(.*)?(\*|\_)(\S+.*)?$/, '$1$3');
          end = end.replace(/^(.*\S+)?(\*|\_)(\s+.*)?$/, '$1$3');
        }
        ed.setLine(pos.line, start + end);
        ed.focus();
        return;
      }
      if (end === null) {
        end = '';
      } else {
        end = end || start;
      }
      text = ed.getSelection();
      pos = ed.getCursor('end');
      pos.ch += start.length;
      ed.replaceSelection(start + text + end);
      ed.setCursor(pos);
      ed.focus();
    };

    var toggleLine = function() {
      var pos = ed.getCursor('start');
      var text = ed.getLine(pos.line);

      var map;
      if (stat[name]) {
        map = {
          quote: /^(\s*)\>\s+/,
          'unordered-list': /^(\s*)(\*|\-|\+)\s+/,
          'ordered-list': /^(\s*)\d+\.\s+/
        };
        text = text.replace(map[name], '$1');
        ed.setLine(pos.line, text);
      } else {
        map = {
          quote: '> ',
          'unordered-list': '* ',
          'ordered-list': '1. '
        };
        ed.setLine(pos.line, map[name] + text);
      }
      ed.focus();
    };

    switch (name) {
      case 'bold':
        replaceSelection('**');
        break;
      case 'italic':
        replaceSelection('*');
        break;
      case 'link':
        replaceSelection('[', '](http://)');
        break;
      case 'image':
        replaceSelection('![', '](http://)');
        break;
      case 'quote':
      case 'unordered-list':
      case 'ordered-list':
        toggleLine();
        break;
      case 'undo':
        ed.undo();
        ed.focus();
        break;
      case 'redo':
        ed.redo();
        ed.focus();
        break;
    }
  };

  exports = module.exports = new Editor();
  exports.Editor = Editor;

  // helpers

  function createIcon(icon) {
    var el;
    if (icon === 'separator') {
      el = document.createElement('i');
      el.className = icon;
      el.innerHTML = '|';
      return el;
    }
    icon = fixIcon(icon);
    el = document.createElement('span');
    el.className = 'icon-' + icon;
    return el;
  }

  function getState(ed) {
    var pos = ed.getCursor('start');
    var stat = ed.getTokenAt(pos);
    if (!stat.type) return {};

    var types = stat.type.split(' ');

    var ret = {}, data, text;
    for (var i = 0; i < types.length; i++) {
      data = types[i];
      if (data === 'strong') {
        ret.bold = true;
      } else if (data === 'variable-2') {
        text = ed.getLine(pos.line);
        if (/^\s*\d+\.\s/.test(text)) {
          ret['ordered-list'] = true;
        } else {
          ret['unordered-list'] = true;
        }
      } else if (data === 'atom') {
        ret.quote = true;
      } else if (data === 'em') {
        ret.italic = true;
      }
    }
    return ret;
  }

  function fixIcon(name) {
    var map = {
      quote: 'quotes-left',
      'ordered-list': 'numbered-list',
      'unordered-list': 'list'
    };
    if (map[name]) return map[name];
    return name;
  }

  function toggleFullScreen() {
    // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
    var doc = document;

    var fullscreenElement = doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement;

    var requestFullscreen = doc.requestFullscreen || doc.mozRequestFullScreen || doc.webkitRequestFullscreen;
  }
});
<commit_msg>you can customize shortcuts now<commit_after>define(function(require, exports, module) {

  function Editor(options) {
    this.init(options);
  }

  Editor.prototype.init = function(options) {
    options = options || {};
    if (options.element) {
      this.element = options.element;
    }
    options.tools = options.tools || [
      'bold', 'italic', 'separator',
      'quote', 'unordered-list', 'ordered-list', 'separator',
      'link', 'image', 'separator',
      'undo', 'redo', 'separator',
      'info', 'expand'
    ];
    options.status = options.status || ['lines', 'words', 'cursor'];

    var isMac = /Mac/.test(navigator.platform);
    var _ = function(text) {
      if (!isMac) return text.replace('Cmd', 'Ctrl');
      return text;
    };

    options.shortcuts = options.shortcuts || {
      bold: _('Cmd-B'),
      italic: _('Cmd-I'),
      link: _('Cmd-L'),
      image: _('Shift-Cmd-I'),
      'ordered-list': _('Shift-Cmd-O'),
      'unordered-list': _('Shift-Cmd-U')
    };
    options.iconmap = options.iconmap || {
      quote: 'quotes-left',
      'ordered-list': 'numbered-list',
      'unordered-list': 'list'
    };
    this.options = options;
  };

  Editor.prototype.render = function(el) {
    if (!el) {
      el = this.element || document.getElementsByTagName('textarea')[0];
    }
    this.element = el;

    var self = this;

    var keyMaps = {};
    var shortcuts = this.options.shortcuts;
    for (var key in shortcuts) {
      (function(key) {
        keyMaps[shortcuts[key]] = function(cm) {
          self.action(key, cm);
        }
      })(key);
    }

    var editor = CodeMirror.fromTextArea(el, {
      mode: 'gfm',
      theme: 'paper',
      indentWithTabs: true,
      lineNumbers: false,
      extraKeys: keyMaps
    });
    this.editor = editor;

    var bar = this.createToolbar();
    this.createStatusbar();

    // ie < 9 sucks
    if (!bar.classList || !bar.querySelector) return;

    editor.on('cursorActivity', function() {
      var icons = bar.getElementsByTagName('span');

      for (var i = 0; i < icons.length; i++) {
        var el = icons[i];
        el.classList.remove('active');
      }

      var stat = getState(editor);
      for (var key in stat) {
        if (stat[key]) {
          el = document.querySelector('.icon-' + fixIcon(key));
          el.classList.add('active');
        }
      }
    });
  };

  Editor.prototype.createToolbar = function(tools) {
    tools = tools || this.options.tools;

    if (!tools || tools.length === 0) return;

    var bar = document.createElement('div');
    bar.className = 'editor-toolbar';

    var el, self = this;
    for (var i = 0; i < tools.length; i++) {
      name = tools[i];
      (function(name) {
        el = createIcon(name);
        // bind events
        el.onclick = function() {
          return self.action(name);
        };
        bar.appendChild(el);
      })(tools[i]);
    }
    var editorElement = this.editor.getWrapperElement();
    editorElement.parentNode.insertBefore(bar, editorElement);
    return bar;
  };

  Editor.prototype.createStatusbar = function(status) {
    status = status || this.options.status;

    if (!status || status.length === 0) return;

    var bar = document.createElement('div');
    bar.className = 'editor-statusbar';

    var pos, editor = this.editor;
    for (var i = 0; i < status.length; i++) {
      (function(name) {
        var el = document.createElement('span');
        el.className = name;
        if (name === 'words') {
          el.innerHTML = '0';
          editor.on('update', function() {
            el.innerHTML = editor.getValue().length;
          });
        } else if (name === 'lines') {
          el.innerHTML = '0';
          editor.on('update', function() {
            el.innerHTML = editor.lineCount();
          });
        } else if (name === 'cursor') {
          el.innerHTML = '0:0';
          editor.on('cursorActivity', function() {
            pos = editor.getCursor();
            el.innerHTML = pos.line + ':' + pos.ch;
          });
        }
        bar.appendChild(el);
      })(status[i]);
    }
    var editorElement = this.editor.getWrapperElement();
    editorElement.parentNode.insertBefore(bar, editorElement.nextSibling);
    return bar;
  };

  Editor.prototype.action = function(name, ed) {
    ed = ed || this.editor;
    if (!ed) return;
    var stat = getState(ed);

    var replaceSelection = function(start, end) {
      var pos, text;
      if (stat[name]) {
        pos = ed.getCursor('start');
        text = ed.getLine(pos.line);
        start = text.slice(0, pos.ch);
        end = text.slice(pos.ch);
        if (name === 'bold') {
          start = start.replace(/^(.*)?(\*|\_){2}(\S+.*)?$/, '$1$3');
          end = end.replace(/^(.*\S+)?(\*|\_){2}(\s+.*)?$/, '$1$3');
        } else if (name === 'italic') {
          start = start.replace(/^(.*)?(\*|\_)(\S+.*)?$/, '$1$3');
          end = end.replace(/^(.*\S+)?(\*|\_)(\s+.*)?$/, '$1$3');
        }
        ed.setLine(pos.line, start + end);
        ed.focus();
        return;
      }
      if (end === null) {
        end = '';
      } else {
        end = end || start;
      }
      text = ed.getSelection();
      pos = ed.getCursor('end');
      pos.ch += start.length;
      ed.replaceSelection(start + text + end);
      ed.setCursor(pos);
      ed.focus();
    };

    var toggleLine = function() {
      var pos = ed.getCursor('start');
      var text = ed.getLine(pos.line);

      var map;
      if (stat[name]) {
        map = {
          quote: /^(\s*)\>\s+/,
          'unordered-list': /^(\s*)(\*|\-|\+)\s+/,
          'ordered-list': /^(\s*)\d+\.\s+/
        };
        text = text.replace(map[name], '$1');
        ed.setLine(pos.line, text);
      } else {
        map = {
          quote: '> ',
          'unordered-list': '* ',
          'ordered-list': '1. '
        };
        ed.setLine(pos.line, map[name] + text);
      }
      ed.focus();
    };

    switch (name) {
      case 'bold':
        replaceSelection('**');
        break;
      case 'italic':
        replaceSelection('*');
        break;
      case 'link':
        replaceSelection('[', '](http://)');
        break;
      case 'image':
        replaceSelection('![', '](http://)');
        break;
      case 'quote':
      case 'unordered-list':
      case 'ordered-list':
        toggleLine();
        break;
      case 'undo':
        ed.undo();
        ed.focus();
        break;
      case 'redo':
        ed.redo();
        ed.focus();
        break;
    }
  };

  exports = module.exports = new Editor();
  exports.Editor = Editor;

  // helpers

  function createIcon(icon) {
    var el;
    if (icon === 'separator') {
      el = document.createElement('i');
      el.className = icon;
      el.innerHTML = '|';
      return el;
    }
    icon = fixIcon(icon);
    el = document.createElement('span');
    el.className = 'icon-' + icon;
    return el;
  }

  function getState(ed) {
    var pos = ed.getCursor('start');
    var stat = ed.getTokenAt(pos);
    if (!stat.type) return {};

    var types = stat.type.split(' ');

    var ret = {}, data, text;
    for (var i = 0; i < types.length; i++) {
      data = types[i];
      if (data === 'strong') {
        ret.bold = true;
      } else if (data === 'variable-2') {
        text = ed.getLine(pos.line);
        if (/^\s*\d+\.\s/.test(text)) {
          ret['ordered-list'] = true;
        } else {
          ret['unordered-list'] = true;
        }
      } else if (data === 'atom') {
        ret.quote = true;
      } else if (data === 'em') {
        ret.italic = true;
      }
    }
    return ret;
  }

  function fixIcon(name) {
    var map = {
      quote: 'quotes-left',
      'ordered-list': 'numbered-list',
      'unordered-list': 'list'
    };
    if (map[name]) return map[name];
    return name;
  }

  function toggleFullScreen() {
    // https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
    var doc = document;

    var fullscreenElement = doc.fullscreenElement || doc.mozFullScreenElement || doc.webkitFullscreenElement;

    var requestFullscreen = doc.requestFullscreen || doc.mozRequestFullScreen || doc.webkitRequestFullscreen;
  }
});
<|endoftext|>


<commit_before>;(function( $ ) {

    /**
     * Module definition that call-forwards to methods defined in a private
     * member array EFX, or initializes Efx on the element specified if no
     * method is defined, or throws a jQuery error if the method specified does
     * not exist.
     */
    $.fn.efx = function(method) {

        // Call-forward to method specified by first argument
        if (EFX[method])
            return EFX[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));

        // Fallback to call-forward to init method
        else if (typeof method === 'object' || !method)
            return EFX.init.apply( this, arguments );

        // Cannot perform a call-forward so throw error
        else
            $.error( 'Method ' +  method + ' does not exist on jQuery.efx' );
    }

    /**
     * Container holding all drivers that have been added.
     */
    var drivers = {}

    /**
     * Methods to which the module will call-forward for $(..).efx(method, ..).
     */
    var EFX = {

        /**
         * Initializes Efx on ele for $(ele).efx() or $(ele).efx('init').
         */
        init: function(){

            init(this);

        },

        /**
         * $(null).efx('add', effect_name, driver) adds driver as the handler
         * for effect_name.
         */
        add: function(effect, driver){

            if(arguments.length < 2 || typeof driver != 'function')
                return;

            drivers[effect] = driver

        },

        /**
         * $(null).efx('add', effect_name) returns true if effect_name defined.
         */
        supports: function(effect){

            return drivers[effect] != undefined

        },

        /**
         * $(null).efx('remove', effect_name) removes the driver effect_name.
         */
        remove: function(effect){

            if(drivers[effect] != undefined)
                delete drivers[effect]

        }
    }

    /**
     * Initializes all targets and their triggers/containers and then attaches
     * an event listener to the triggers that will delegate to the proper
     * driver upon the trigger being initiated.
     */
    function init(ele){

        // Find all triggers (elements with a data-target)
        var triggers = ele.find('[data-target]')

        // Initialize the triggers and their targets
        triggers.each(function(){
            var trigger = $(this)
            targets_of(trigger).each(function(){
                init_effect_on($(this), trigger)
            })
        })

        // Attach listeners to the triggers
        triggers.click(delegate)

    }

    /**
     * Click handler that executes a trigger across its targets.
     */
    function delegate(){

        var trigger = $(this)
        targets_of(trigger).each(function(){
            execute_effect_on($(this), trigger)
        })

    }

    /**
     * Determines all targets of a trigger element.
     */
    function targets_of(trigger){

        var target = trigger.data('target')

        // Shorthand for ID neglects the # sign since HTML elements not allowed
        if(target.match(/^[A-Za-z]/))
            target = '#'+target;

        return $(target)

    }

    /**
     * Runs the init method of the driver for the trigger and its respective
     * targets/containers.
     */
    function init_effect_on(target, trigger){

        var driver = driver_for(target, trigger);
        if(driver != undefined && driver.init != undefined)
            driver.init();

    }

    /**
     * Runs the init method of the driver for the trigger and its respective
     * targets/containers.
     */
    function execute_effect_on(target, trigger){

        var driver = driver_for(target, trigger);
        if(driver != undefined && driver.execute != undefined)
            driver.execute();

    }

    /**
     * Factory that constructs a driver for a given target. Trigger is also
     * provided as an argument since it too is passed to the driver.
     */
    function driver_for(target, trigger){

        var effect = false;
        var container = target;

        // Determine if the the target has a valid effect
        if(effect = target.data('effect'))
            if(!EFX.supports(effect))
                effect = false

        // Search parents for a valid effect if not on target itself
        if(!effect){
            var parents = target.parents('[data-effect]')
            for(var i = 0; i < parents.length && !effect; i++){
                container = $(parents[i])
                effect = container.data('effect')
                if(!EFX.supports(effect))
                    effect = false
            }
        }

        // Builds a driver if effect could be resolved or false otherwise
        return effect ? new drivers[effect]({'target':target, 'container':container, 'trigger':trigger}) : false

    }

})( jQuery );<commit_msg>Add engine support for multiple events [fixes #1]<commit_after>;(function( $ ) {

    /**
     * Module definition that call-forwards to methods defined in a private
     * member array EFX, or initializes Efx on the element specified if no
     * method is defined, or throws a jQuery error if the method specified does
     * not exist.
     */
    $.fn.efx = function(method) {

        // Call-forward to method specified by first argument
        if (_EFX[method])
            return _EFX[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));

        // Fallback to call-forward to init method
        else if (typeof method === 'object' || !method)
            return _EFX.init.apply( this, arguments );

        // Cannot perform a call-forward so throw error
        else
            $.error( 'Method ' +  method + ' does not exist on jQuery.efx' );
    }

    /**
     * Container holding all drivers that have been added.
     */
    var _drivers = {}

    /**
     * Container that stores all events that have been registered to any driver.
     */
    var _events = []

    var _has_initialized = false;

    /**
     * Methods to which the module will call-forward for $(..).efx(method, ..).
     */
    var _EFX = {

        /**
         * Initializes Efx on ele for $(ele).efx() or $(ele).efx('init').
         */
        init: function(){

            init(this)

            _has_initialized = true

        },

        /**
         * $(null).efx('add', effect_name, driver) adds driver as the handler
         * for effect_name.
         */
        add: function(effect, event, driver){

            if(arguments.length < 3 || typeof driver != 'function')
                return;

            if(_drivers[effect] == undefined)
                _drivers[effect] = {}

            _drivers[effect][event] = driver

            if($.inArray(event, _events) < 0){

                _events.push(event)

                /** @todo make it possible to add drivers after initialization */
                if(_has_initialized)
                    console.log('[Efx] WARNING Driver event added after listeners have already been attached to some element')

            }
        },

        /**
         * $(null).efx('add', effect_name) returns true if effect_name defined.
         */
        supports: function(effect, event){

            return _drivers[effect] != undefined && ( event == undefined || _drivers[effect][event] != undefined )

        },

        /**
         * $(null).efx('remove', effect_name) removes the driver effect_name.
         */
        remove: function(effect, event){

            if(event == undefined){
                if(_drivers[effect] != undefined)
                    delete _drivers[effect]
            }else{
                if(_drivers[effect][event] != undefined)
                    delete _drivers[effect][event]
            }

        }
    }

    /**
     * Initializes all targets and their triggers/containers and then attaches
     * an event listener to the triggers that will delegate to the proper
     * driver upon the trigger being initiated.
     */
    function init(ele){

        // Find all triggers (elements with a data-target)
        var triggers = ele.find('[data-target]')

        // Initialize the triggers and their targets
        triggers.each(function(){
            var trigger = $(this)
            targets_of(trigger).each(function(){
                execute_effect_on('init',$(this), trigger)
            })
        })

        $(_events).each(function(){
            if(this != 'init')
                triggers.bind(this+'', delegate)
        })

    }

    /**
     * Click handler that executes a trigger across its targets.
     */
    function delegate(event){

        var trigger = $(this)
        targets_of(trigger).each(function(){
            execute_effect_on(event.type, $(this), trigger)
        })

    }

    /**
     * Determines all targets of a trigger element.
     */
    function targets_of(trigger){

        var target = trigger.data('target')

        // Shorthand for ID neglects the # sign since HTML elements not allowed
        if(target.match(/^[A-Za-z]/))
            target = '#'+target;

        return $(target)

    }

    /**
     * Runs the init method of the driver for the trigger and its respective
     * targets/containers.
     */
    function execute_effect_on(event, target, trigger){

        var effect = false;
        var container = target;

        // Determine if the the target has a valid effect
        if(effect = target.data('effect'))
            if(!_EFX.supports(effect, event))
                effect = false

        // Search parents for a valid effect if not on target itself
        if(!effect){
            var parents = target.parents('[data-effect]')
            for(var i = 0; i < parents.length && !effect; i++){
                container = $(parents[i])
                effect = container.data('effect')
                if(!_EFX.supports(effect, event))
                    effect = false
            }
        }

        // Builds a driver if effect could be resolved or false otherwise
        if(effect){
            _drivers[effect][event]({'target':target, 'container':container, 'trigger':trigger})
        }

    }

})( jQuery );<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import BnfParser from '../generated/bnf-parser.gen';
import GrammarMode from './grammar-mode';
import GrammarSymbol from './grammar-symbol';
import LexGrammar from './lex-grammar';
import LexRule from './lex-rule';
import Production from './production';

import colors from 'colors';
import fs from 'fs';
import vm from 'vm';

import debug from '../debug';

/**
 * Class encapsulates operations with a grammar.
 */
export default class Grammar {

  /**
   * A grammar may be passed as an object with `lex` and `bnf` properties.
   * The `lex` part is a set of rules for the lexer, and `bnf` is actual
   * context-free grammar. If `start` property is passed, it's used as the
   * start symbol, otherwise it's inferred from the first production.
   *
   * const grammar = {
   *
   *   // Lexical grammar
   *   // format: <regexp rule>: token
   *   // The token can either be a raw string value, like "foo",
   *   // or a variable (written in ALL_CAPITALIZED notation), which
   *   // can be used further in the `bnf` grammar.
   *
   *   "lex": {
   *     "macros": {
   *       "digit": "[0-9]",
   *     },
   *
   *     "rules": [
   *       ["a", "return 'a';"],
   *       ["\\(", "return '(';"],
   *       ["\\)", "return ')';"],
   *       ["\\+", "return '+';"],
   *       ["{digit}+(\\.{digit}+)?\\b", "return 'NUMBER';"],
   *
   *       // A rule with start conditions. Such rules are matched only
   *       // when a scanner enters these states.
   *       [["string", "code"], '[^"]',  "return 'STRING';"],
   *     ],
   *
   *     "startConditions": { https://gist.github.com/DmitrySoshnikov/f5e2583b37e8f758c789cea9dcdf238a
   *       "string": 1, // inclusive condition %s
   *       "code": 0,   // exclusive consition %x
   *     },
   *   },
   *
   *   // Arbitrary code to be included in the generated parser.
   *
   *   "moduleInclude": "const AST = require('./ast');"
   *
   *   "tokens": "a ( ) + NUMBER",
   *
   *   "start": "S",
   *
   *   // BNF grammar
   *
   *   bnf: {
   *     "S": [ "F",
   *            "( S + F )" ],
   *     "F": [ "a",
   *            "NUMBER" ],
   *   }
   * };
   *
   * Note: the "bnf" can also be passed as a string:
   *
   *   bnf: `
   *     S -> F
   *        | ( S + F )
   *
   *     F -> a
   *        | NUMBER
   *   `
   *
   * Note: if no `lex` is provided, the lexical grammar is inferred
   * from the list of all terminals in the `bnf` grammar.
   */
  constructor({
    /**
     * Lexical grammar.
     */
    lex,

    /**
     * Explicit list of tokens. If not provided, it's inferred automatically
     * from the BNF grammar.
     */
    tokens,

    /**
     * BNF grammar.
     */
    bnf,

    /**
     * Precedence, and associativity.
     */
    operators,

    /**
     * Start symbol. If not provided, it's inferred from the first
     * production's LHS.
     */
    start,

    /**
     * Grammar mode (LL1, SLR1, LALR1, etc).
     */
    mode,

    /**
     * Source code which should be included at the beginning of
     * the generated parser.
     */
    moduleInclude = '',

    /**
     * Whether to capture locations.
     */
    captureLocations = false,
  }) {
    this._mode = new GrammarMode(mode);
    this._startSymbol = start;

    this._captureLocations = captureLocations;

    // Operators and precedence.
    this._operators = this._processOperators(operators);

    // Actual BNF grammar.
    this._originalBnf = bnf;
    this._bnf = this._processBnf(this._originalBnf);

    // Injecting user code, including handlers for `yyparse.onParseBegin`,
    // and `yyparse.onParseEnd`.
    this._moduleInclude = moduleInclude;

    this._nonTerminals = this.getNonTerminals();
    this._terminals = this.getTerminals();

    if (typeof tokens === 'string') {
      tokens = tokens.split(/\s+/);
    }

    this._tokens = Array.isArray(tokens)
      ? tokens.map(token => GrammarSymbol.get(token))
      : this.getTokens();

    // Lexical grammar.
    this._lexGrammar = this._createLexGrammar(lex);
    this._tokens = this._processTokens(tokens);

    // Caching maps.
    this._productionsForSymbol = {};
    this._productionsWithSymbol = {};
  }

  /**
   * Loads a grammar object from a grammar file,
   * for the specific options.
   */
  static fromGrammarFile(grammarFile, options = {}, grammarType = 'bnf') {
    const grammarData = Grammar.dataFromGrammarFile(grammarFile, grammarType);
    return Grammar.fromData(grammarData, options);
  }

  /**
   * Reads grammar file data. Supports reading `bnf`,
   * and `lex` grammars based on mode.
   */
  static dataFromGrammarFile(grammarFile, grammarType = 'bnf') {
    return Grammar.dataFromString(
      fs.readFileSync(grammarFile, 'utf-8'),
      grammarType,
    );
  }

  /**
   * Creates Grammar instance from grammar data for
   * a particular parsing options.
   */
  static fromData(grammarData, options = {}) {
    return new Grammar({...grammarData, ...options});
  }

  /**
   * Creates Grammar instance from grammar string.
   */
  static fromString(string, options = {}, grammarType = 'bnf') {
    const grammarData = Grammar.dataFromString(string, grammarType);
    return Grammar.fromData(grammarData, options);
  }

  /**
   * Generates data from grammar string.
   */
  static dataFromString(grammarString, grammarType = 'bnf') {
    let grammarData = null;

    debug.time('Grammar loaded in');

    try {
      // Pure JSON representation.
      grammarData = JSON.parse(grammarString);
      debug.log(`Grammar (${grammarType}) is in JSON format`);
    } catch (e) {
      // JS code.
      try {
        grammarData = vm.runInNewContext(`
          (function() { return (${grammarString});})()
        `);
        debug.log(`Grammar (${grammarType}) is in JS format`);
      } catch (jsEx) {
        const jsError = jsEx.stack;
        // A grammar as a string, for BNF, and lex.
        if (grammarType) {
          try {
            grammarData = BnfParser.parse(grammarString);
            debug.log(`Grammar (${grammarType}) is in BNF format`);
          } catch (bnfEx) {
            console.error(
              colors.red('\nParsing grammar in JS-format failed:\n\n') +
              jsError +'\n',
            );
            console.error(
              colors.red('\nParsing grammar in BNF-format failed:\n\n'),
            );
            throw bnfEx;
          }
        }
      }
    }

    debug.timeEnd('Grammar loaded in');
    return grammarData;
  }

  /**
   * Returns associated lexical grammar.
   */
  getLexGrammar() {
    return this._lexGrammar;
  }

  /**
   * Returns Start symbol of this grammar (it's initialized
   * during normalization process).
   */
  getStartSymbol() {
    return this._startSymbol;
  }

  /**
   * Returns grammar mode.
   */
  getMode() {
    return this._mode;
  }

  /**
   * Returns module include code.
   */
  getModuleInclude() {
    return this._moduleInclude;
  }

  /**
   * Whther should capture locations.
   */
  shouldCaptureLocations() {
    return this._captureLocations;
  }

  /**
   * Returns precedence and associativity of operators.
   */
  getOperators() {
    return this._operators;
  }

  /**
   * Returns list of terminals in this grammar.
   */
  getTerminals() {
    if (!this._terminals) {
      this._terminals = [];

      let terminals = {};

      this._bnf.forEach(production => {
        production.getRHS().forEach(symbol => {
          if (symbol.isTerminal() &&
              !terminals.hasOwnProperty(symbol.getSymbol())) {
            terminals[symbol.getSymbol()] = true;
            this._terminals.push(symbol);
          }
        });
      });
    }

    return this._terminals;
  }

  /**
   * Returns terminal symbols.
   */
  getTerminalSymbols() {
    if (!this._terminalSymbols) {
      this._terminalSymbols = this.getTerminals()
        .map(symbol => symbol.getSymbol());
    }
    return this._terminalSymbols;
  }

  /**
   * Returns list of non-terminals in this grammar.
   */
  getNonTerminals() {
    if (!this._nonTerminals) {
      this._nonTerminals = [];

      this._nonTerminalsMap = {};

      this._bnf.forEach(production => {
        if (production.isAugmented()) {
          return;
        }
        let nonTerminal = production.getLHS();
        if (!this._nonTerminalsMap.hasOwnProperty(nonTerminal.getSymbol())) {
          this._nonTerminalsMap[nonTerminal.getSymbol()] = true;
          this._nonTerminals.push(nonTerminal);
        }
      });
    }

    return this._nonTerminals;
  }

  /**
   * Returns list of non-terminal symbols.
   */
  getNonTerminalSymbols() {
    if (!this._nonTerminalSymbols) {
      this._nonTerminalSymbols = this.getNonTerminals()
        .map(symbol => symbol.getSymbol());
    }
    return this._nonTerminalSymbols;
  }

  /**
   * Returns tokens. Infer tokens from the grammar if
   * they were not passed explicitly.
   */
  getTokens() {
    if (!this._tokens) {
      this._tokens = [];

      let tokensMap = {};

      this._bnf.forEach(production => {
        if (production.isAugmented() || production.isEpsilon()) {
          return;
        }
        production.getRHS().forEach(symbol => {
          let rawSymbol = symbol.getSymbol();
          if (!symbol.isTerminal() &&
              !this._nonTerminalsMap.hasOwnProperty(rawSymbol) &&
              !tokensMap.hasOwnProperty(rawSymbol)) {
            tokensMap[rawSymbol] = true;
            this._tokens.push(symbol);
          }
        });
      });
    }

    return this._tokens;
  }

  /**
   * Returns token symbols.
   */
  getTokenSymbols() {
    if (!this._tokenSymbols) {
      this._tokenSymbols = this.getTokens()
        .map(symbol => symbol.getSymbol());
    }
    return this._tokenSymbols;
  }

  /**
   * Returns grammar productions.
   */
  getProductions() {
    return this._bnf;
  }

  /**
   * Returns productions for a specific non-terminal.
   */
  getProductionsForSymbol(symbol) {
    if (symbol instanceof GrammarSymbol) {
      symbol = symbol.getSymbol();
    }

    if (!this._productionsForSymbol.hasOwnProperty(symbol)) {
      this._productionsForSymbol[symbol] = this._bnf.filter(production => {
        return production.getLHS().isSymbol(symbol);
      });
    }

    return this._productionsForSymbol[symbol];
  }

  /**
   * Returns productions where a non-terminal is used (appears on RHS).
   */
  getProductionsWithSymbol(symbol) {
    if (symbol instanceof GrammarSymbol) {
      symbol = symbol.getSymbol();
    }

    if (!this._productionsWithSymbol.hasOwnProperty(symbol)) {
      this._productionsWithSymbol[symbol] = this._bnf.filter(production => {
        return production.getRHS().some(s => s.getSymbol() === symbol);
      });
    }

    return this._productionsWithSymbol[symbol];
  }

  /**
   * Gets a production by number.
   */
  getProduction(number) {
    // LL grammars do not have augmented 0-production.
    return this._bnf[this._mode.isLL() ? number - 1 : number];
  }

  /**
   * Returns an augmented production (used in LR parsers),
   * which is built during normalization process. The augmented
   * production is always the first one.
   */
  getAugmentedProduction() {
    if (!this._mode.isLR()) {
      throw new TypeError(
        `Augmented production is built only for LR grammars`
      );
    }
    return this._bnf[0];
  }

  /**
   * Tokens are either raw text values like "foo", or
   * one of the variables from the lexical grammar.
   */
  isTokenSymbol(symbol) {
    if (!(symbol instanceof GrammarSymbol)) {
      symbol = GrammarSymbol.get(symbol);
    }

    return symbol.isTerminal() ||
      this.getTokens().some(token => {
        return token.getSymbol() === symbol.getSymbol();
      });
  }

  /**
   * Pretty prints the grammar.
   */
  print() {
    console.info('\nGrammar:\n');

    let pad = '    ';
    let productions = this.getProductions();
    let numberPad = productions.length.toString().length;

    productions.forEach(production => {
      let productionOutput =
        `${pad}${this._padLeft(production.getNumber(), numberPad)}. ` +
        production.toString();

      console.info(productionOutput);

      if (this._mode.isLR() && production.isAugmented()) {
        let splitter = Array(productionOutput.length - 2).join('-');
        console.info(`${pad}${splitter}`);
      }
    });
  }

  _padLeft(value, times) {
    value = value.toString();
    let spaces = Array(times - value.length + 1).join(' ');
    return spaces + value;
  }

  _processOperators(operators) {
    let processedOperators = {};

    if (operators) {
      operators.forEach((opData, i) => {
        opData.slice(1).forEach(op => {
          processedOperators[op] = {
            precedence: i + 1,
            assoc: opData[0],
          };
        })
      });
    }

    return processedOperators;
  }

  /**
   * Generates data arrays for lex rules inferred from terminals.
   */
  _generateLexRulesDataForTerminals() {
    return this.getTerminals().map(terminal => [
      LexRule.matcherFromTerminal(terminal.getSymbol()), // matcher
      `return ${terminal.quotedTerminal()};`, // token handler
    ]);
  }

  /**
   * Creates lex grammar instance.
   */
  _createLexGrammar(lex) {
    if (!lex) {
      lex = {
        rules: [],
      };
    }

    // Infer automatic lex-rules from raw terminals
    // (symbols in quotes) in BNF productions RHS.
    lex.rules.push(...this._generateLexRulesDataForTerminals());

    return new LexGrammar(lex);
  }

  /**
   * Processes tokens.
   */
  _processTokens(tokens) {
    if (typeof tokens === 'string') {
      tokens = tokens.split(/\s+/);
    }

    return Array.isArray(tokens)
      ? tokens.map(token => GrammarSymbol.get(token))
      : this.getTokens();
  }

  _processBnf(originalBnf) {
    let processedBnf = [];
    let nonTerminals = Object.keys(originalBnf);

    // LR grammar uses augmented 0-production.
    let number = this._mode.isLR() ? 0 : 1;

    if (!this._startSymbol) {
      this._startSymbol = nonTerminals[0];
    }

    if (this._mode.isLR()) {
      // Augmented rule, $accept -> S.
      let augmentedProduction = new Production({
        LHS: '$accept',
        RHS: this._startSymbol,
        number: number++,
        grammar: this,
      });
      processedBnf[0] = augmentedProduction;
    }

    nonTerminals.forEach(LHS => {
      originalBnf[LHS].forEach((RHS, k) => {
        let semanticAction = null;
        let precedence = null;

        if (Array.isArray(RHS)) {
          let precedenceTag = null;

          if (typeof RHS[1] === 'string') {
            semanticAction = RHS[1];
            if (RHS[2] !== null && typeof RHS[2] === 'object') {
              precedenceTag = RHS[2].prec;
            }
          } else if (RHS[1] !== null && typeof RHS[1] === 'object') {
            precedenceTag = RHS[1].prec;
          }

          RHS = RHS[0];

          if (precedenceTag && this._operators) {
            precedence = this._operators[precedenceTag].precedence;
          }
        }

        processedBnf.push(new Production({
          LHS,
          RHS,
          semanticAction,
          precedence,
          number: number++,
          isShort: k > 0,
          grammar: this,
        }));
      });
    });

    return processedBnf;
  }
};
<commit_msg>Use tokenMap for faster access<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

import BnfParser from '../generated/bnf-parser.gen';
import GrammarMode from './grammar-mode';
import GrammarSymbol from './grammar-symbol';
import LexGrammar from './lex-grammar';
import LexRule from './lex-rule';
import Production from './production';

import colors from 'colors';
import fs from 'fs';
import vm from 'vm';

import debug from '../debug';

/**
 * Class encapsulates operations with a grammar.
 */
export default class Grammar {

  /**
   * A grammar may be passed as an object with `lex` and `bnf` properties.
   * The `lex` part is a set of rules for the lexer, and `bnf` is actual
   * context-free grammar. If `start` property is passed, it's used as the
   * start symbol, otherwise it's inferred from the first production.
   *
   * const grammar = {
   *
   *   // Lexical grammar
   *   // format: <regexp rule>: token
   *   // The token can either be a raw string value, like "foo",
   *   // or a variable (written in ALL_CAPITALIZED notation), which
   *   // can be used further in the `bnf` grammar.
   *
   *   "lex": {
   *     "macros": {
   *       "digit": "[0-9]",
   *     },
   *
   *     "rules": [
   *       ["a", "return 'a';"],
   *       ["\\(", "return '(';"],
   *       ["\\)", "return ')';"],
   *       ["\\+", "return '+';"],
   *       ["{digit}+(\\.{digit}+)?\\b", "return 'NUMBER';"],
   *
   *       // A rule with start conditions. Such rules are matched only
   *       // when a scanner enters these states.
   *       [["string", "code"], '[^"]',  "return 'STRING';"],
   *     ],
   *
   *     "startConditions": { https://gist.github.com/DmitrySoshnikov/f5e2583b37e8f758c789cea9dcdf238a
   *       "string": 1, // inclusive condition %s
   *       "code": 0,   // exclusive consition %x
   *     },
   *   },
   *
   *   // Arbitrary code to be included in the generated parser.
   *
   *   "moduleInclude": "const AST = require('./ast');"
   *
   *   "tokens": "a ( ) + NUMBER",
   *
   *   "start": "S",
   *
   *   // BNF grammar
   *
   *   bnf: {
   *     "S": [ "F",
   *            "( S + F )" ],
   *     "F": [ "a",
   *            "NUMBER" ],
   *   }
   * };
   *
   * Note: the "bnf" can also be passed as a string:
   *
   *   bnf: `
   *     S -> F
   *        | ( S + F )
   *
   *     F -> a
   *        | NUMBER
   *   `
   *
   * Note: if no `lex` is provided, the lexical grammar is inferred
   * from the list of all terminals in the `bnf` grammar.
   */
  constructor({
    /**
     * Lexical grammar.
     */
    lex,

    /**
     * Explicit list of tokens. If not provided, it's inferred automatically
     * from the BNF grammar.
     */
    tokens,

    /**
     * BNF grammar.
     */
    bnf,

    /**
     * Precedence, and associativity.
     */
    operators,

    /**
     * Start symbol. If not provided, it's inferred from the first
     * production's LHS.
     */
    start,

    /**
     * Grammar mode (LL1, SLR1, LALR1, etc).
     */
    mode,

    /**
     * Source code which should be included at the beginning of
     * the generated parser.
     */
    moduleInclude = '',

    /**
     * Whether to capture locations.
     */
    captureLocations = false,
  }) {
    this._mode = new GrammarMode(mode);
    this._startSymbol = start;

    this._captureLocations = captureLocations;

    // Operators and precedence.
    this._operators = this._processOperators(operators);

    // Actual BNF grammar.
    this._originalBnf = bnf;
    this._bnf = this._processBnf(this._originalBnf);

    // Injecting user code, including handlers for `yyparse.onParseBegin`,
    // and `yyparse.onParseEnd`.
    this._moduleInclude = moduleInclude;

    this._nonTerminals = this.getNonTerminals();
    this._terminals = this.getTerminals();

    if (typeof tokens === 'string') {
      tokens = tokens.split(/\s+/);
    }

    this._tokens = Array.isArray(tokens)
      ? tokens.map(token => GrammarSymbol.get(token))
      : this.getTokens();

    // Lexical grammar.
    this._lexGrammar = this._createLexGrammar(lex);
    this._tokens = this._processTokens(tokens);

    // Caching maps.
    this._productionsForSymbol = {};
    this._productionsWithSymbol = {};
  }

  /**
   * Loads a grammar object from a grammar file,
   * for the specific options.
   */
  static fromGrammarFile(grammarFile, options = {}, grammarType = 'bnf') {
    const grammarData = Grammar.dataFromGrammarFile(grammarFile, grammarType);
    return Grammar.fromData(grammarData, options);
  }

  /**
   * Reads grammar file data. Supports reading `bnf`,
   * and `lex` grammars based on mode.
   */
  static dataFromGrammarFile(grammarFile, grammarType = 'bnf') {
    return Grammar.dataFromString(
      fs.readFileSync(grammarFile, 'utf-8'),
      grammarType,
    );
  }

  /**
   * Creates Grammar instance from grammar data for
   * a particular parsing options.
   */
  static fromData(grammarData, options = {}) {
    return new Grammar({...grammarData, ...options});
  }

  /**
   * Creates Grammar instance from grammar string.
   */
  static fromString(string, options = {}, grammarType = 'bnf') {
    const grammarData = Grammar.dataFromString(string, grammarType);
    return Grammar.fromData(grammarData, options);
  }

  /**
   * Generates data from grammar string.
   */
  static dataFromString(grammarString, grammarType = 'bnf') {
    let grammarData = null;

    debug.time('Grammar loaded in');

    try {
      // Pure JSON representation.
      grammarData = JSON.parse(grammarString);
      debug.log(`Grammar (${grammarType}) is in JSON format`);
    } catch (e) {
      // JS code.
      try {
        grammarData = vm.runInNewContext(`
          (function() { return (${grammarString});})()
        `);
        debug.log(`Grammar (${grammarType}) is in JS format`);
      } catch (jsEx) {
        const jsError = jsEx.stack;
        // A grammar as a string, for BNF, and lex.
        if (grammarType) {
          try {
            grammarData = BnfParser.parse(grammarString);
            debug.log(`Grammar (${grammarType}) is in BNF format`);
          } catch (bnfEx) {
            console.error(
              colors.red('\nParsing grammar in JS-format failed:\n\n') +
              jsError +'\n',
            );
            console.error(
              colors.red('\nParsing grammar in BNF-format failed:\n\n'),
            );
            throw bnfEx;
          }
        }
      }
    }

    debug.timeEnd('Grammar loaded in');
    return grammarData;
  }

  /**
   * Returns associated lexical grammar.
   */
  getLexGrammar() {
    return this._lexGrammar;
  }

  /**
   * Returns Start symbol of this grammar (it's initialized
   * during normalization process).
   */
  getStartSymbol() {
    return this._startSymbol;
  }

  /**
   * Returns grammar mode.
   */
  getMode() {
    return this._mode;
  }

  /**
   * Returns module include code.
   */
  getModuleInclude() {
    return this._moduleInclude;
  }

  /**
   * Whther should capture locations.
   */
  shouldCaptureLocations() {
    return this._captureLocations;
  }

  /**
   * Returns precedence and associativity of operators.
   */
  getOperators() {
    return this._operators;
  }

  /**
   * Returns list of terminals in this grammar.
   */
  getTerminals() {
    if (!this._terminals) {
      this._terminals = [];

      let terminals = {};

      this._bnf.forEach(production => {
        production.getRHS().forEach(symbol => {
          if (symbol.isTerminal() &&
              !terminals.hasOwnProperty(symbol.getSymbol())) {
            terminals[symbol.getSymbol()] = true;
            this._terminals.push(symbol);
          }
        });
      });
    }

    return this._terminals;
  }

  /**
   * Returns terminal symbols.
   */
  getTerminalSymbols() {
    if (!this._terminalSymbols) {
      this._terminalSymbols = this.getTerminals()
        .map(symbol => symbol.getSymbol());
    }
    return this._terminalSymbols;
  }

  /**
   * Returns list of non-terminals in this grammar.
   */
  getNonTerminals() {
    if (!this._nonTerminals) {
      this._nonTerminals = [];

      this._nonTerminalsMap = {};

      this._bnf.forEach(production => {
        if (production.isAugmented()) {
          return;
        }
        let nonTerminal = production.getLHS();
        if (!this._nonTerminalsMap.hasOwnProperty(nonTerminal.getSymbol())) {
          this._nonTerminalsMap[nonTerminal.getSymbol()] = true;
          this._nonTerminals.push(nonTerminal);
        }
      });
    }

    return this._nonTerminals;
  }

  /**
   * Returns list of non-terminal symbols.
   */
  getNonTerminalSymbols() {
    if (!this._nonTerminalSymbols) {
      this._nonTerminalSymbols = this.getNonTerminals()
        .map(symbol => symbol.getSymbol());
    }
    return this._nonTerminalSymbols;
  }

  /**
   * Returns tokens. Infer tokens from the grammar if
   * they were not passed explicitly.
   */
  getTokens() {
    if (!this._tokens) {
      this._tokens = [];

      this._tokensMap = {};

      this._bnf.forEach(production => {
        if (production.isAugmented() || production.isEpsilon()) {
          return;
        }
        production.getRHS().forEach(symbol => {
          let rawSymbol = symbol.getSymbol();
          if (!symbol.isTerminal() &&
              !this._nonTerminalsMap.hasOwnProperty(rawSymbol) &&
              !this._tokensMap.hasOwnProperty(rawSymbol)) {
            this._tokensMap[rawSymbol] = true;
            this._tokens.push(symbol);
          }
        });
      });
    }

    return this._tokens;
  }

  /**
   * Returns token symbols.
   */
  getTokenSymbols() {
    if (!this._tokenSymbols) {
      this._tokenSymbols = this.getTokens()
        .map(symbol => symbol.getSymbol());
    }
    return this._tokenSymbols;
  }

  /**
   * Returns grammar productions.
   */
  getProductions() {
    return this._bnf;
  }

  /**
   * Returns productions for a specific non-terminal.
   */
  getProductionsForSymbol(symbol) {
    if (symbol instanceof GrammarSymbol) {
      symbol = symbol.getSymbol();
    }

    if (!this._productionsForSymbol.hasOwnProperty(symbol)) {
      this._productionsForSymbol[symbol] = this._bnf.filter(production => {
        return production.getLHS().isSymbol(symbol);
      });
    }

    return this._productionsForSymbol[symbol];
  }

  /**
   * Returns productions where a non-terminal is used (appears on RHS).
   */
  getProductionsWithSymbol(symbol) {
    if (symbol instanceof GrammarSymbol) {
      symbol = symbol.getSymbol();
    }

    if (!this._productionsWithSymbol.hasOwnProperty(symbol)) {
      this._productionsWithSymbol[symbol] = this._bnf.filter(production => {
        return production.getRHS().some(s => s.getSymbol() === symbol);
      });
    }

    return this._productionsWithSymbol[symbol];
  }

  /**
   * Gets a production by number.
   */
  getProduction(number) {
    // LL grammars do not have augmented 0-production.
    return this._bnf[this._mode.isLL() ? number - 1 : number];
  }

  /**
   * Returns an augmented production (used in LR parsers),
   * which is built during normalization process. The augmented
   * production is always the first one.
   */
  getAugmentedProduction() {
    if (!this._mode.isLR()) {
      throw new TypeError(
        `Augmented production is built only for LR grammars`
      );
    }
    return this._bnf[0];
  }

  /**
   * Tokens are either raw text values like "foo", or
   * one of the variables from the lexical grammar.
   */
  isTokenSymbol(symbol) {
    if (!(symbol instanceof GrammarSymbol)) {
      symbol = GrammarSymbol.get(symbol);
    }

    return symbol.isTerminal() ||
      this._tokensMap.hasOwnProperty(symbol.getSymbol());
  }

  /**
   * Pretty prints the grammar.
   */
  print() {
    console.info('\nGrammar:\n');

    let pad = '    ';
    let productions = this.getProductions();
    let numberPad = productions.length.toString().length;

    productions.forEach(production => {
      let productionOutput =
        `${pad}${this._padLeft(production.getNumber(), numberPad)}. ` +
        production.toString();

      console.info(productionOutput);

      if (this._mode.isLR() && production.isAugmented()) {
        let splitter = Array(productionOutput.length - 2).join('-');
        console.info(`${pad}${splitter}`);
      }
    });
  }

  _padLeft(value, times) {
    value = value.toString();
    let spaces = Array(times - value.length + 1).join(' ');
    return spaces + value;
  }

  _processOperators(operators) {
    let processedOperators = {};

    if (operators) {
      operators.forEach((opData, i) => {
        opData.slice(1).forEach(op => {
          processedOperators[op] = {
            precedence: i + 1,
            assoc: opData[0],
          };
        })
      });
    }

    return processedOperators;
  }

  /**
   * Generates data arrays for lex rules inferred from terminals.
   */
  _generateLexRulesDataForTerminals() {
    return this.getTerminals().map(terminal => [
      LexRule.matcherFromTerminal(terminal.getSymbol()), // matcher
      `return ${terminal.quotedTerminal()};`, // token handler
    ]);
  }

  /**
   * Creates lex grammar instance.
   */
  _createLexGrammar(lex) {
    if (!lex) {
      lex = {
        rules: [],
      };
    }

    // Infer automatic lex-rules from raw terminals
    // (symbols in quotes) in BNF productions RHS.
    lex.rules.push(...this._generateLexRulesDataForTerminals());

    return new LexGrammar(lex);
  }

  /**
   * Processes tokens.
   */
  _processTokens(tokens) {
    if (typeof tokens === 'string') {
      tokens = tokens.split(/\s+/);
    }

    return Array.isArray(tokens)
      ? tokens.map(token => GrammarSymbol.get(token))
      : this.getTokens();
  }

  _processBnf(originalBnf) {
    let processedBnf = [];
    let nonTerminals = Object.keys(originalBnf);

    // LR grammar uses augmented 0-production.
    let number = this._mode.isLR() ? 0 : 1;

    if (!this._startSymbol) {
      this._startSymbol = nonTerminals[0];
    }

    if (this._mode.isLR()) {
      // Augmented rule, $accept -> S.
      let augmentedProduction = new Production({
        LHS: '$accept',
        RHS: this._startSymbol,
        number: number++,
        grammar: this,
      });
      processedBnf[0] = augmentedProduction;
    }

    nonTerminals.forEach(LHS => {
      originalBnf[LHS].forEach((RHS, k) => {
        let semanticAction = null;
        let precedence = null;

        if (Array.isArray(RHS)) {
          let precedenceTag = null;

          if (typeof RHS[1] === 'string') {
            semanticAction = RHS[1];
            if (RHS[2] !== null && typeof RHS[2] === 'object') {
              precedenceTag = RHS[2].prec;
            }
          } else if (RHS[1] !== null && typeof RHS[1] === 'object') {
            precedenceTag = RHS[1].prec;
          }

          RHS = RHS[0];

          if (precedenceTag && this._operators) {
            precedence = this._operators[precedenceTag].precedence;
          }
        }

        processedBnf.push(new Production({
          LHS,
          RHS,
          semanticAction,
          precedence,
          number: number++,
          isShort: k > 0,
          grammar: this,
        }));
      });
    });

    return processedBnf;
  }
};
<|endoftext|>


<commit_before>require("./main.less");
import {$, Vue} from "../libs.js";

new Vue({
    el: ".main-app",
    template: require("./main.hbs"),

    data: {
        tests: [
            {id: "vector-features", label: "Vector Features", selected: false},
            {id: "overlay-elements", label: "Overlay Elements", selected: false}
        ],
        selectedTest: {}
    },

    created: function () {
        var initTest = this._findById(this._hashValue()) || this.tests[0];
        this._selectTest(initTest);
    },

    methods: {
        runTest: function (test) {
            this._selectTest(test);
        },

        _selectTest: function (test) {
            this._unselectAll();
            test.selected = true;
            this.selectedTest = test;
        },

        _unselectAll: function () {
            this.tests.forEach(test => test.selected = false);
        },

        _findById: function (id) {
            return this.tests.filter(test => test.id == id)[0];
        },

        _hashValue: function () {
            return window.location.hash.replace(/^#/, "");
        }
    }
});<commit_msg>added topics for tests<commit_after>require("./main.less");
import {$, Vue} from "../libs.js";

new Vue({
    el: ".main-app",
    template: require("./main.hbs"),

    data: {
        tests: [
            {id: "vector-features", label: "Vector Features", selected: false},
            {id: "overlay-elements", label: "Overlay Elements", selected: false},
            {id: "animating-features", label: "Animating Features", selected: false},
            {id: "hit-detection", label: "Hit Detection", selected: false},
            {id: "topo-json", label: "TopoJSON", selected: false}
        ],
        selectedTest: {}
    },

    created: function () {
        var initTest = this._findById(this._hashValue()) || this.tests[0];
        this._selectTest(initTest);
    },

    methods: {
        runTest: function (test) {
            this._selectTest(test);
        },

        _selectTest: function (test) {
            this._unselectAll();
            test.selected = true;
            this.selectedTest = test;
        },

        _unselectAll: function () {
            this.tests.forEach(test => test.selected = false);
        },

        _findById: function (id) {
            return this.tests.filter(test => test.id == id)[0];
        },

        _hashValue: function () {
            return window.location.hash.replace(/^#/, "");
        }
    }
});<|endoftext|>


<commit_before>KISSY.add("brix/core/pagelet", function(S, Chunk) {
    /**
     * 用以给brick打上id的标记,brick有id则返回
     * @method _stamp
     * @param el
     * @return {string}
     * @ignore
     */

    function _stamp(el) {
        if (!el.attr('id')) {
            var id;
            //判断页面id是否存在，如果存在继续随机。
            while ((id = S.guid('brix_brick_')) && S.one('#' + id)) {}
            el.attr('id', id);
        }
        return el.attr('id');
    }
    /**
     * Brix Pagelet 是组件的管理器，实现组件的渲染。
     * 一个页面由多个组件和非组件的HTML片段组成，实际创建过程中需要一个个动态创建，
     * 基于约定为大的原则，采用“钩子”和模板引擎，自动化的完成组件渲染和行为附加
     * @extends Brix.Chunk
     * @class Brix.Pagelet
     */
    var Pagelet = Chunk.extend({
        initializer: function() {
            var self = this;
            //初始化属性
            self.isReady = false;
            self.readyList = [];
            self.bricks = [];
            self.isAddBehavior = false;
            self.destroyed = false; //是否销毁的标志位。
        },
        bindUI: function() {
            //增加参数回调
            var self = this;
            var callback = self.get('callback');
            if (callback && typeof callback === 'function') {
                self.ready(callback);
            }
            //自动添加行为渲染
            if (self.get('autoBehavior')) {
                self.addBehavior();
            }
        },
        /**
         * 根据dom id，获取brick的实例
         * @param  {String} id     brick的id
         * @return {Object}        组件实例
         */
        getBrick: function(id) {
            var self = this;
            var brick = null;
            S.each(self.bricks, function(b) {
                if (b.id === id) {
                    brick = b.brick;
                    return false;
                }
            });
            return brick;
        },
        /**
         * 根据bx-name，获取brick的实例数组
         * @param  {String} name     brick的bx-name
         * @return {Array}           组件实例数组
         */
        getBricks: function(name) {
            var self = this;
            var bricks = [];
            S.each(self.bricks, function(b) {
                if (b.name === name) {
                    bricks.push(b.brick);
                }
            });
            return bricks;
        },
        /**
         * 销毁组件
         * @param {String} id 组件id
         */
        destroyBrick: function(id) {
            var self = this;
            for (var i = 0; i < self.bricks.length; i++) {
                var o = self.bricks[i];
                if (id === o.id) {
                    self._bx_destroyBrick(o);
                    self.bricks.splice(i, 1);
                    return false;
                }
            }
        },
        /**
         * 销毁brick引用
         * @param  {Object} o 需要销毁的对象
         * @private
         */
        _bx_destroyBrick: function(o) {
            o.destroyed = true;
            if (o.brick) {
                o.brick.destroy && o.brick.destroy();
                o.brick = null;
            }
        },
        /**
         * 给组件添加行为
         */
        addBehavior: function() {
            var self = this;
            if (self.get('rendered') && !self.isAddBehavior) {
                self.isAddBehavior = true;
                var el = self.get('el');
                var brickNodes = el.all('[bx-name]');
                if (el.hasAttr('bx-name')) {
                    brickNodes = el.add(brickNodes);
                }
                self._bx_addBehavior(brickNodes, function(bricks) {
                    self.bricks = bricks;
                }, function() {
                    self.on('beforeRefreshTmpl', function(e) {
                        if (e.renderType === 'html') {
                            e.node.all('[bx-name]').each(function(node) {
                                self.destroyBrick(node.attr('id'));
                            });
                        }
                    });
                    self.on('afterRefreshTmpl', function(e) {
                        self._bx_addBehavior(e.node.all('[bx-name]'), function(newBricks) {
                            if (newBricks.length > 0) {
                                self.bricks = self.bricks.concat(newBricks);
                            }
                        }, function() {
                            self._bx_fireReady();
                        });
                    });
                    self._bx_fireReady();
                });
            }
        },

        /**
         * 给组件添加行为
         * @param {NodeList} brickNodes 组件node对象集合
         * @param {Function} fn 页面元素解析完成执行的方法,同步执行
         * @param {Function} callback 实例化完成后的回调事件，异步执行
         * @private
         */
        _bx_addBehavior: function(brickNodes, fn, callback) {
            var self = this;
            var bxConfig = self.get('config');
            var bricks = [];
            self.isReady = false;
            brickNodes.each(function(brickNode) {
                if (brickNode.attr('bx-behavior') != 'true') {
                    var id = _stamp(brickNode),
                        name = brickNode.attr('bx-name'),
                        path = brickNode.attr('bx-path'),
                        config = Brix.returnJSON(brickNode.attr('bx-config'));
                    if (bxConfig && bxConfig[id]) {
                        S.mix(config, bxConfig[id]);
                    }
                    brickNode.attr('bx-behavior', 'true');
                    bricks.push({
                        id: id,
                        name: name,
                        path: path,
                        config: config
                    });
                }
            });

            //构建pagelet需要引用组件js
            if (bricks.length > 0) {
                var useList = [];
                S.each(bricks, function(o) {
                    if (!o.path) {
                        o.path = 'brix/gallery/' + o.name + '/';
                    }
                    if (!S.inArray(useList, o.path) && !o.config.autoBrick) {
                        useList.push(o.path);
                    }
                });
                /**
                 * @event beforeAddBehavior
                 * fired before component is instantiated
                 * @param {KISSY.Event.CustomEventObject} e
                 */
                self.fire('beforeAddBehavior', {
                    useList: useList
                });
                fn && fn(bricks);
                //实例化pagelet所有组件
                S.use(useList.join(','), function(S) {
                    if (self.destroyed) {
                        return;
                    }
                    var useClassList = arguments;
                    S.each(bricks, function(o) {
                        if (!o.destroyed && !o.config.autoBrick) {
                            var id = o.id;
                            var config = S.merge({
                                container: '#' + id,
                                el: '#' + id,
                                pagelet: self
                            }, o.config);
                            var TheBrick = useClassList[S.indexOf(o.path, useList) + 1];
                            var myBrick = new TheBrick(config);
                            o.brick = myBrick;
                        }
                    });
                    /**
                     * @event afterAddBehavior
                     * fired after component is instantiated
                     * @param {KISSY.Event.CustomEventObject} e
                     */
                    self.fire('afterAddBehavior', {
                        useList: useList,
                        bricks: bricks
                    });
                    useList = null;
                    useClassList = null;
                    callback && callback();
                });
            } else {
                fn && fn(bricks);
                callback && callback();
            }

        },

        /**
         * 渲染完成后需要执行的函数
         * @param {Function} fn 执行的函数
         */
        ready: function(fn) {
            if (this.isReady) {
                fn.call(window, this);
            } else {
                this.readyList.push(fn);
            }
        },
        /**
         * 触发ready添加的方法
         * @private
         */
        _bx_fireReady: function() {
            var self = this;
            if (self.isReady) {
                return;
            }
            self.isReady = true;
            //局部变量，保证所有注册方法只执行一次
            var readyList = self.readyList;
            self.readyList = [];
            if (readyList.length > 0) {
                var fn, i = 0;
                while (fn = readyList[i++]) {
                    fn.call(self);
                }
            }
            readyList = null;
        },
        destructor: function() {
            var self = this;
            S.each(self.bricks, function(o, i) {
                self._bx_destroyBrick(o);
            });
            self.bricks = null;
            if (self.get('rendered')) {
                var action = self.get('destroyAction');
                var el = self.get('el');
                switch (action) {
                    case 'remove':
                        el.remove();
                        break;
                    case 'empty':
                        el.empty();
                        break;
                }
                el = null;
            }
            self.destroyed = true;
        }
    }, {
        ATTRS: {
            /**
             * 自动添加组件行为
             * @cfg {Boolean}
             */
            autoBehavior: {
                value: true
            },
            /**
             * 行为添加完成后的回调方法
             * @cfg {Function}
             */
            callback: {
                value: null
            },
            /**
             * 增加pagelet对brick组件的配置增强,示例：{id:{xx:{},yy:{}}}
             * @cfg {Object}
             */
            config: {
                value: {}
            }
        }
    }, 'Pagelet');
    return Pagelet;
}, {
    requires: ['./chunk']
});<commit_msg>fixed fire ready bug<commit_after>KISSY.add("brix/core/pagelet", function(S, Chunk) {
    /**
     * 用以给brick打上id的标记,brick有id则返回
     * @method _stamp
     * @param el
     * @return {string}
     * @ignore
     */

    function _stamp(el) {
        if (!el.attr('id')) {
            var id;
            //判断页面id是否存在，如果存在继续随机。
            while ((id = S.guid('brix_brick_')) && S.one('#' + id)) {}
            el.attr('id', id);
        }
        return el.attr('id');
    }
    /**
     * Brix Pagelet 是组件的管理器，实现组件的渲染。
     * 一个页面由多个组件和非组件的HTML片段组成，实际创建过程中需要一个个动态创建，
     * 基于约定为大的原则，采用“钩子”和模板引擎，自动化的完成组件渲染和行为附加
     * @extends Brix.Chunk
     * @class Brix.Pagelet
     */
    var Pagelet = Chunk.extend({
        initializer: function() {
            var self = this;
            //初始化属性
            self.isReady = false;
            self.readyList = [];
            self.bricks = [];
            self.isAddBehavior = false;
            self.destroyed = false; //是否销毁的标志位。
            self.bxCounter = 0;
        },
        bindUI: function() {
            //增加参数回调
            var self = this;
            var callback = self.get('callback');
            if (callback && typeof callback === 'function') {
                self.ready(callback);
            }
            //自动添加行为渲染
            if (self.get('autoBehavior')) {
                self.addBehavior();
            }
        },
        /**
         * 根据dom id，获取brick的实例
         * @param  {String} id     brick的id
         * @return {Object}        组件实例
         */
        getBrick: function(id) {
            var self = this;
            var brick = null;
            S.each(self.bricks, function(b) {
                if (b.id === id) {
                    brick = b.brick;
                    return false;
                }
            });
            return brick;
        },
        /**
         * 根据bx-name，获取brick的实例数组
         * @param  {String} name     brick的bx-name
         * @return {Array}           组件实例数组
         */
        getBricks: function(name) {
            var self = this;
            var bricks = [];
            S.each(self.bricks, function(b) {
                if (b.name === name) {
                    bricks.push(b.brick);
                }
            });
            return bricks;
        },
        /**
         * 销毁组件
         * @param {String} id 组件id
         */
        destroyBrick: function(id) {
            var self = this;
            for (var i = 0; i < self.bricks.length; i++) {
                var o = self.bricks[i];
                if (id === o.id) {
                    self._bx_destroyBrick(o);
                    self.bricks.splice(i, 1);
                    return false;
                }
            }
        },
        /**
         * 销毁brick引用
         * @param  {Object} o 需要销毁的对象
         * @private
         */
        _bx_destroyBrick: function(o) {
            o.destroyed = true;
            if (o.brick) {
                o.brick.destroy && o.brick.destroy();
                o.brick = null;
            }
        },
        /**
         * 给组件添加行为
         */
        addBehavior: function() {
            var self = this;
            if (self.get('rendered') && !self.isAddBehavior) {
                self.isAddBehavior = true;
                var el = self.get('el');
                var brickNodes = el.all('[bx-name]');
                if (el.hasAttr('bx-name')) {
                    brickNodes = el.add(brickNodes);
                }
                self._bx_addBehavior(brickNodes, function(bricks) {
                    self.bricks = bricks;
                }, function() {
                    self.on('beforeRefreshTmpl', function(e) {
                        self.bxCounter++;
                        if (e.renderType === 'html') {
                            e.node.all('[bx-name]').each(function(node) {
                                self.destroyBrick(node.attr('id'));
                            });
                        }
                    });
                    self.on('afterRefreshTmpl', function(e) {
                        self._bx_addBehavior(e.node.all('[bx-name]'), function(newBricks) {
                            if (newBricks.length > 0) {
                                self.bricks = self.bricks.concat(newBricks);
                            }
                        }, function() {
                            self.bxCounter--;
                            if(self.bxCounter===0){
                                self._bx_fireReady();
                            }
                        });
                    });
                    self._bx_fireReady();
                });
            }
        },

        /**
         * 给组件添加行为
         * @param {NodeList} brickNodes 组件node对象集合
         * @param {Function} fn 页面元素解析完成执行的方法,同步执行
         * @param {Function} callback 实例化完成后的回调事件，异步执行
         * @private
         */
        _bx_addBehavior: function(brickNodes, fn, callback) {
            var self = this;
            var bxConfig = self.get('config');
            var bricks = [];
            self.isReady = false;
            brickNodes.each(function(brickNode) {
                if (brickNode.attr('bx-behavior') != 'true') {
                    var id = _stamp(brickNode),
                        name = brickNode.attr('bx-name'),
                        path = brickNode.attr('bx-path'),
                        config = Brix.returnJSON(brickNode.attr('bx-config'));
                    if (bxConfig && bxConfig[id]) {
                        S.mix(config, bxConfig[id]);
                    }
                    brickNode.attr('bx-behavior', 'true');
                    bricks.push({
                        id: id,
                        name: name,
                        path: path,
                        config: config
                    });
                }
            });

            //构建pagelet需要引用组件js
            if (bricks.length > 0) {
                var useList = [];
                S.each(bricks, function(o) {
                    if (!o.path) {
                        o.path = 'brix/gallery/' + o.name + '/';
                    }
                    if (!S.inArray(useList, o.path) && !o.config.autoBrick) {
                        useList.push(o.path);
                    }
                });
                /**
                 * @event beforeAddBehavior
                 * fired before component is instantiated
                 * @param {KISSY.Event.CustomEventObject} e
                 */
                self.fire('beforeAddBehavior', {
                    useList: useList
                });
                fn && fn(bricks);
                //实例化pagelet所有组件
                S.use(useList.join(','), function(S) {
                    if (self.destroyed) {
                        return;
                    }
                    var useClassList = arguments;
                    S.each(bricks, function(o) {
                        if (!o.destroyed && !o.config.autoBrick) {
                            var id = o.id;
                            var config = S.merge({
                                container: '#' + id,
                                el: '#' + id,
                                pagelet: self
                            }, o.config);
                            var TheBrick = useClassList[S.indexOf(o.path, useList) + 1];
                            var myBrick = new TheBrick(config);
                            o.brick = myBrick;
                        }
                    });
                    /**
                     * @event afterAddBehavior
                     * fired after component is instantiated
                     * @param {KISSY.Event.CustomEventObject} e
                     */
                    self.fire('afterAddBehavior', {
                        useList: useList,
                        bricks: bricks
                    });
                    useList = null;
                    useClassList = null;
                    callback && callback();
                });
            } else {
                fn && fn(bricks);
                callback && callback();
            }

        },

        /**
         * 渲染完成后需要执行的函数
         * @param {Function} fn 执行的函数
         */
        ready: function(fn) {
            if (this.isReady) {
                fn.call(window, this);
            } else {
                this.readyList.push(fn);
            }
        },
        /**
         * 触发ready添加的方法
         * @private
         */
        _bx_fireReady: function() {
            var self = this;
            if (self.isReady) {
                return;
            }
            self.isReady = true;
            //局部变量，保证所有注册方法只执行一次
            var readyList = self.readyList;
            self.readyList = [];
            if (readyList.length > 0) {
                var fn, i = 0;
                while (fn = readyList[i++]) {
                    fn.call(self);
                }
            }
            readyList = null;
        },
        destructor: function() {
            var self = this;
            S.each(self.bricks, function(o, i) {
                self._bx_destroyBrick(o);
            });
            self.bricks = null;
            if (self.get('rendered')) {
                var action = self.get('destroyAction');
                var el = self.get('el');
                switch (action) {
                    case 'remove':
                        el.remove();
                        break;
                    case 'empty':
                        el.empty();
                        break;
                }
                el = null;
            }
            self.destroyed = true;
        }
    }, {
        ATTRS: {
            /**
             * 自动添加组件行为
             * @cfg {Boolean}
             */
            autoBehavior: {
                value: true
            },
            /**
             * 行为添加完成后的回调方法
             * @cfg {Function}
             */
            callback: {
                value: null
            },
            /**
             * 增加pagelet对brick组件的配置增强,示例：{id:{xx:{},yy:{}}}
             * @cfg {Object}
             */
            config: {
                value: {}
            }
        }
    }, 'Pagelet');
    return Pagelet;
}, {
    requires: ['./chunk']
});<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import shallowCompare from 'react-addons-shallow-compare'
import classNames from 'classnames'
import { connect } from 'react-redux'
import { get } from 'lodash'
import { getCategories } from '../actions/discover'
import { loadNotifications } from '../actions/notifications'
import { loadProfile } from '../actions/profile'
import * as MAPPING_TYPES from '../constants/mapping_types'
import DevTools from '../components/devtools/DevTools'
import { AppHelmet } from '../components/helmets/AppHelmet'
import Modal from '../components/modals/Modal'
import Omnibar from '../components/omnibar/Omnibar'
import { addGlobalDrag, removeGlobalDrag } from '../components/viewport/GlobalDrag'
import { startRefreshTimer } from '../components/viewport/RefreshOnFocus'
import AnalyticsContainer from '../containers/AnalyticsContainer'
import EditorToolsContainer from '../containers/EditorToolsContainer'
import FooterContainer from '../containers/FooterContainer'
import KeyboardContainer from '../containers/KeyboardContainer'
import NavbarContainer from '../containers/NavbarContainer'
import ViewportContainer from '../containers/ViewportContainer'
import { findModel } from '../helpers/json_helper'

class AppContainer extends Component {

  static propTypes = {
    authentication: PropTypes.object.isRequired,
    children: PropTypes.node.isRequired,
    dispatch: PropTypes.func.isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string,
    }).isRequired,
    pagination: PropTypes.shape({
      next: PropTypes.string,
    }),
    pathname: PropTypes.string.isRequired,
    params: PropTypes.shape({
      username: PropTypes.string,
      token: PropTypes.string,
      type: PropTypes.string,
    }).isRequired,
  }

  componentDidMount() {
    addGlobalDrag()
    startRefreshTimer()
    const { dispatch } = this.props
    if (get(this.props, 'authentication.isLoggedIn')) {
      dispatch(loadProfile())
      dispatch(loadNotifications({ category: 'all' }))
    }
    dispatch(getCategories())
  }

  componentWillReceiveProps(nextProps) {
    const prevAuthentication = this.props.authentication
    const { authentication, dispatch } = nextProps
    if (authentication &&
        !prevAuthentication.isLoggedIn &&
        authentication.isLoggedIn) {
      dispatch(loadProfile())
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState)
  }

  componentWillUnmount() {
    removeGlobalDrag()
  }

  render() {
    const { authentication, children, params, pagination, pathname } = this.props
    const { isLoggedIn } = authentication
    const appClasses = classNames(
      'AppContainer',
      { isLoggedIn },
      { isLoggedOut: !isLoggedIn },
    )
    return (

        <AppHelmet pagination={pagination} pathname={pathname} />
        <ViewportContainer routerParams={params} />
        {isLoggedIn ? <Omnibar /> : null}
        {children}
        <NavbarContainer routerParams={params} />
        <FooterContainer />
        {isLoggedIn ? <EditorToolsContainer /> : null}
        <Modal />
        <DevTools />
        <KeyboardContainer />
        <AnalyticsContainer />

    )
  }
}

AppContainer.preRender = (store) => {
  const state = store.getState()
  if (state.authentication && state.authentication.isLoggedIn) {
    return Promise.all([
      store.dispatch(getCategories()),
      store.dispatch(loadProfile()),
    ])
  }
  return store.dispatch(getCategories())
}

const PAGING_BLACKLIST = [
  /^\/enter\b/,
  /^\/forgot-password\b/,
  /^\/join\b/,
  /^\/signup\b/,
  /^\/following$/,
  /^\/starred$/,
  /^\/notifications\b/,
  /^\/settings\b/,
  /^\/onboarding\b/,
  /^\/invitations\b/,
]

function isPagingEnabled(pathname) {
  for (const re of PAGING_BLACKLIST) {
    if (re.test(pathname)) {
      return false
    }
  }
  return true
}

function mapStateToProps(state, ownProps) {
  const { authentication, json } = state
  let pagination = null
  if (state.json.pages && isPagingEnabled(ownProps.location.pathname)) {
    let result = state.json.pages[ownProps.location.pathname]
    if (!result && ownProps.params.token) {
      // determine if we are on a post detail
      // to find the comment pagination
      const post = findModel(json, {
        collection: MAPPING_TYPES.POSTS,
        findObj: { token: ownProps.params.token.toLowerCase() },
      })
      if (post) {
        result = state.json.pages[`/posts/${post.id}/comments`]
      }
    }
    if (result && result.pagination) {
      pagination = result.pagination
    }
  }
  return {
    authentication,
    pagination,
    pathname: ownProps.location.pathname,
  }
}

export default connect(mapStateToProps)(AppContainer)

<commit_msg>Remove the pre fetching of categories<commit_after>import React, { Component, PropTypes } from 'react'
import shallowCompare from 'react-addons-shallow-compare'
import classNames from 'classnames'
import { connect } from 'react-redux'
import { get } from 'lodash'
import { getCategories } from '../actions/discover'
import { loadNotifications } from '../actions/notifications'
import { loadProfile } from '../actions/profile'
import * as MAPPING_TYPES from '../constants/mapping_types'
import DevTools from '../components/devtools/DevTools'
import { AppHelmet } from '../components/helmets/AppHelmet'
import Modal from '../components/modals/Modal'
import Omnibar from '../components/omnibar/Omnibar'
import { addGlobalDrag, removeGlobalDrag } from '../components/viewport/GlobalDrag'
import { startRefreshTimer } from '../components/viewport/RefreshOnFocus'
import AnalyticsContainer from '../containers/AnalyticsContainer'
import EditorToolsContainer from '../containers/EditorToolsContainer'
import FooterContainer from '../containers/FooterContainer'
import KeyboardContainer from '../containers/KeyboardContainer'
import NavbarContainer from '../containers/NavbarContainer'
import ViewportContainer from '../containers/ViewportContainer'
import { findModel } from '../helpers/json_helper'

class AppContainer extends Component {

  static propTypes = {
    authentication: PropTypes.object.isRequired,
    children: PropTypes.node.isRequired,
    dispatch: PropTypes.func.isRequired,
    location: PropTypes.shape({
      pathname: PropTypes.string,
    }).isRequired,
    pagination: PropTypes.shape({
      next: PropTypes.string,
    }),
    pathname: PropTypes.string.isRequired,
    params: PropTypes.shape({
      username: PropTypes.string,
      token: PropTypes.string,
      type: PropTypes.string,
    }).isRequired,
  }

  componentDidMount() {
    addGlobalDrag()
    startRefreshTimer()
    const { dispatch } = this.props
    if (get(this.props, 'authentication.isLoggedIn')) {
      dispatch(loadProfile())
      dispatch(loadNotifications({ category: 'all' }))
    }
    dispatch(getCategories())
  }

  componentWillReceiveProps(nextProps) {
    const prevAuthentication = this.props.authentication
    const { authentication, dispatch } = nextProps
    if (authentication &&
        !prevAuthentication.isLoggedIn &&
        authentication.isLoggedIn) {
      dispatch(loadProfile())
    }
  }

  shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState)
  }

  componentWillUnmount() {
    removeGlobalDrag()
  }

  render() {
    const { authentication, children, params, pagination, pathname } = this.props
    const { isLoggedIn } = authentication
    const appClasses = classNames(
      'AppContainer',
      { isLoggedIn },
      { isLoggedOut: !isLoggedIn },
    )
    return (

        <AppHelmet pagination={pagination} pathname={pathname} />
        <ViewportContainer routerParams={params} />
        {isLoggedIn ? <Omnibar /> : null}
        {children}
        <NavbarContainer routerParams={params} />
        <FooterContainer />
        {isLoggedIn ? <EditorToolsContainer /> : null}
        <Modal />
        <DevTools />
        <KeyboardContainer />
        <AnalyticsContainer />

    )
  }
}

AppContainer.preRender = (store) => {
  const state = store.getState()
  if (state.authentication && state.authentication.isLoggedIn) {
    return store.dispatch(loadProfile())
  }
  return null
}

const PAGING_BLACKLIST = [
  /^\/enter\b/,
  /^\/forgot-password\b/,
  /^\/join\b/,
  /^\/signup\b/,
  /^\/following$/,
  /^\/starred$/,
  /^\/notifications\b/,
  /^\/settings\b/,
  /^\/onboarding\b/,
  /^\/invitations\b/,
]

function isPagingEnabled(pathname) {
  for (const re of PAGING_BLACKLIST) {
    if (re.test(pathname)) {
      return false
    }
  }
  return true
}

function mapStateToProps(state, ownProps) {
  const { authentication, json } = state
  let pagination = null
  if (state.json.pages && isPagingEnabled(ownProps.location.pathname)) {
    let result = state.json.pages[ownProps.location.pathname]
    if (!result && ownProps.params.token) {
      // determine if we are on a post detail
      // to find the comment pagination
      const post = findModel(json, {
        collection: MAPPING_TYPES.POSTS,
        findObj: { token: ownProps.params.token.toLowerCase() },
      })
      if (post) {
        result = state.json.pages[`/posts/${post.id}/comments`]
      }
    }
    if (result && result.pagination) {
      pagination = result.pagination
    }
  }
  return {
    authentication,
    pagination,
    pathname: ownProps.location.pathname,
  }
}

export default connect(mapStateToProps)(AppContainer)

<|endoftext|>