<commit_before>/*
 * gmodule
 * https://github.com/goliatone/gmodule
 *
 * Copyright (c) 2013 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
(function (root, name, deps, factory) {
    "use strict";
    if (typeof exports === 'object') {
        // Node
         if(typeof deps === 'function') {
            factory = deps;
            deps = [];
        }
        module.exports = factory.apply(root, deps.map(require));
    } else if (typeof define === 'function' && 'amd' in define) {
        //require js
        define(name, deps, factory);
    } else {
        // Browser
        var d, i = 0, global = root, old = global[name], mod;
        while((d = deps[i]) !== undefined) deps[i++] = root[d];
        global[name] = mod = factory.apply(global, deps);
        //Export no 'conflict module', aliases the module.
        mod.noConflict = function(){
            global[name] = old;
            return mod;
        };
    }
    //TODO: Get rid of jquery!
}(this, "gmodule", function() {

    //TODO: How do we get a reference to the global object here?
    var _namespace = this; //module.config().namespace || this;
    var _exportName = 'Module'; //module.config().exportName || 'Module';

    var _splice = Array.prototype.splice;

    var _isArray = function(obj){
        return obj.toString() === '[object Array]';
    };

    var _merge = function(a, b){
        for(var p in b){
            if(b.hasOwnProperty(p))
                a[p] = b[p];
        }
        return a;
    };

    var Module = function(name, parent){
        // if(_namespace[name])
            // return name;

        //Lets figure out parent.
        //TODO: Add support for com.domain.Module.
        //parent = parent.split('.');
        //while(target = parent.pop()) parent = _namespace[target];
        parent = parent || Module;

        if(typeof parent === 'string'){
            parent = _namespace[parent];
        }

        var self = function(){
            if( 'init' in this) this.init.apply(this, arguments);
        };

        //Change self proto.
        //TODO: We could/should remove this conditional.
        //parent will always be at least Module.
        if(parent){
            for( var i in parent){
                if(parent.hasOwnProperty(i)){
                    self[i] = Module.clone(parent[i]);
                }
            }

            for( i in parent.prototype){
                if(parent.prototype.hasOwnProperty(i)){
                    self.prototype[i] = Module.clone(parent.prototype[i]);
                }
            }

            var Ctor = function(){
                this.ctor = this.constructor = self;
            };
            Ctor.prototype = parent.prototype;
            self.prototype = new Ctor();

            //We need to create super after proto.
            //self._super = parent;
            self.prototype._super = parent.prototype;

            ///////////////////////////////////////
            //TODO: Should we move this out of the constructor?!
            ///////////////////////////////////////
            //Add default constructor stub method.
            //TODO: Rename?
            self.prototype.init = function(){};

            //Override toString method to display class name.
            self.prototype.toString = function(){
                return "[object "+this.__name__+"]"
            };
        }

    //------------------------------
    // Adding class/static properties: i.e: User.findByPk().
        self.extend = function(obj, target){
            /*target = target || self;
            var extended = obj.extended;
            for(var i in obj){
                if(obj.hasOwnProperty(i))
                    target[i] = obj[i];
            }

            if(extended) extended.call(target,target);
            return self;*/

            var args;
            if(arguments.length > 2 ){
                target = obj;
                args = _splice.call(arguments,0);
            } else {
                args = [obj];
            }

            target = target || self;
            var _extend = function(){
                var extended = obj.extended;
                for(var i in obj){
                    if(obj.hasOwnProperty(i))
                        target[i] = obj[i];
                }

                if(extended) extended.call(target,target);
            };

            var i = 0, t = args.length;
            for(;i<t;i++){
                _extend(args[i]);
            }

            return target;
        };
    //  -----------------------------------
    //  Adding instance properties- user.id = 23;
        /**
         * All properties of the provided object will be
         * copied into the prototype of all Module instances.
         *
         * @access  public
         * @param   Object  Template with properties to include.
         * @return  Object  Instance, fluid interface.
         */
        self.include = function(){
            var _include = function(obj){
                var included = obj.included;
                for(var i in obj){
                    if(obj.hasOwnProperty(i))
                        self.fn[i] = obj[i];
                }
                if(included) included.call(self.fn, self.fn);
            };

            var i = 0, t = arguments.length;
            for(;i<t;i++){
                _include(arguments[i]);
            }

            return self;
        };

        /**
         * Utility method to proxy function calls
         * with the proper scope.
         * Any extra parameters passed to it, will be
         * concatenated into the final call.
         *
         * @access public
         * @param   Function    Function to be proxied.
         * @return  Function    Wrapped function with scope set to self.
         */
        self.proxy = function(func){
            var a = _splice.call(arguments,1);
            var self = this;
            return function(){
                var a2 = _splice.call(arguments,0);
                return func.apply(self, a.concat(a2));
            };
        };

        //shortcuts.
        self.fn = self.prototype;
        self.fn.parent = parent; //TODO: We most likely do not need this, since we have now _super.
        self.fn.proxy  = self.proxy;

        // The class/parent name
        self.prototype.__name__  = self.__name__  = name;
        self.prototype.__class__ = self;

        //Store a reference by name in the provided namespace.
        _namespace[name] = self;

        /**
         * Hook to execute "extending" method on parent
         * after we create module. This allows for DRY
         * "extending" implementations.
         * @see  'the "extending" method allows to DRY extended' in
         *       specs.
         */
        if('extending' in self) self.extending();

        return self;
    };

    Module.__name__    = 'Module';
    Module.__version__ = '0.1.3';

    //TODO: Remove?!
    Module.decorator = function(implementation){
        var self = this;
        var Decorator = function(){};
        Decorator.prototype.decorate = function(){
            var i = 0,
                t = arguments.length;
            for(;i < t; i++){
                implementation( arguments[i], this);
            }
        };

        Decorator.prototype.owner = self;

        return new Decorator(self);
    };

    /**
     * Override a method, by default the source object
     * is the current modules prototype.
     * If the source object is not a Module, we create a _super
     * object and attach the original method.
     *
     *
     * @param  {Function}   src
     * @param  {Function}   method
     * @param  {Function} fn
     * @return {void}
     */
    Module.override = function(method, fn, src){
        src = src || this.prototype;
        if(!('_super' in src)){
            src._super = {};
            src._super[method] = src[method];
        }
        src[method] = fn;
    };

    /**
     * Utility method to clone an object.
     *
     * @access  public.
     * @param   object  Object to be cloned.
     * @return  object  Cloned object.
     */
    Module.clone = function(src){
        if (typeof src === 'function') return src;
        if (typeof src !== 'object') return src;
        var out = _isArray(src) ? [] : {};
        return _merge(out, src);
    };

    /*Module.merge = function(){
        console.log(_splice.call(arguments,0))
        return jQuery.extend.apply(jQuery, _splice.call(arguments,0));
    };*/

    /**
     * Scope in which the module will store all clases,
     * to prevent pollution of the global namespace.
     *
     * @access public
     * @param   object  scoped namespace for the module.
     * @return  object  scoped namespace for the module.
     */
    Module.namespace = function(ns){
        var namespace = ns || _namespace;
        if(!namespace.hasOwnProperty(_exportName))
            namespace[_exportName] = Module;
        return namespace;
    };

/*
    Module.prototype.init = function(){
        console.log('Peperone');
        return 'This is just a stub!';
    };*/

    return Module;
}));<commit_msg>Bump version.<commit_after>/*
 * gmodule
 * https://github.com/goliatone/gmodule
 *
 * Copyright (c) 2013 goliatone
 * Licensed under the MIT license.
 */
/* jshint strict: false, plusplus: true */
/*global define: false, require: false, module: false, exports: false */
(function (root, name, deps, factory) {
    "use strict";
    if (typeof exports === 'object') {
        // Node
         if(typeof deps === 'function') {
            factory = deps;
            deps = [];
        }
        module.exports = factory.apply(root, deps.map(require));
    } else if (typeof define === 'function' && 'amd' in define) {
        //require js
        define(name, deps, factory);
    } else {
        // Browser
        var d, i = 0, global = root, old = global[name], mod;
        while((d = deps[i]) !== undefined) deps[i++] = root[d];
        global[name] = mod = factory.apply(global, deps);
        //Export no 'conflict module', aliases the module.
        mod.noConflict = function(){
            global[name] = old;
            return mod;
        };
    }
    //TODO: Get rid of jquery!
}(this, "gmodule", function() {

    //TODO: How do we get a reference to the global object here?
    var _namespace = this; //module.config().namespace || this;
    var _exportName = 'Module'; //module.config().exportName || 'Module';

    var _splice = Array.prototype.splice;

    var _isArray = function(obj){
        return obj.toString() === '[object Array]';
    };

    var _merge = function(a, b){
        for(var p in b){
            if(b.hasOwnProperty(p))
                a[p] = b[p];
        }
        return a;
    };

    var Module = function(name, parent){
        // if(_namespace[name])
            // return name;

        //Lets figure out parent.
        //TODO: Add support for com.domain.Module.
        //parent = parent.split('.');
        //while(target = parent.pop()) parent = _namespace[target];
        parent = parent || Module;

        if(typeof parent === 'string'){
            parent = _namespace[parent];
        }

        var self = function(){
            if( 'init' in this) this.init.apply(this, arguments);
        };

        //Change self proto.
        //TODO: We could/should remove this conditional.
        //parent will always be at least Module.
        if(parent){
            for( var i in parent){
                if(parent.hasOwnProperty(i)){
                    self[i] = Module.clone(parent[i]);
                }
            }

            for( i in parent.prototype){
                if(parent.prototype.hasOwnProperty(i)){
                    self.prototype[i] = Module.clone(parent.prototype[i]);
                }
            }

            var Ctor = function(){
                this.ctor = this.constructor = self;
            };
            Ctor.prototype = parent.prototype;
            self.prototype = new Ctor();

            //We need to create super after proto.
            //self._super = parent;
            self.prototype._super = parent.prototype;

            ///////////////////////////////////////
            //TODO: Should we move this out of the constructor?!
            ///////////////////////////////////////
            //Add default constructor stub method.
            //TODO: Rename?
            self.prototype.init = function(){};

            //Override toString method to display class name.
            self.prototype.toString = function(){
                return "[object "+this.__name__+"]"
            };
        }

    //------------------------------
    // Adding class/static properties: i.e: User.findByPk().
        self.extend = function(obj, target){
            /*target = target || self;
            var extended = obj.extended;
            for(var i in obj){
                if(obj.hasOwnProperty(i))
                    target[i] = obj[i];
            }

            if(extended) extended.call(target,target);
            return self;*/

            var args;
            if(arguments.length > 2 ){
                target = obj;
                args = _splice.call(arguments,0);
            } else {
                args = [obj];
            }

            target = target || self;
            var _extend = function(){
                var extended = obj.extended;
                for(var i in obj){
                    if(obj.hasOwnProperty(i))
                        target[i] = obj[i];
                }

                if(extended) extended.call(target,target);
            };

            var i = 0, t = args.length;
            for(;i<t;i++){
                _extend(args[i]);
            }

            return target;
        };
    //  -----------------------------------
    //  Adding instance properties- user.id = 23;
        /**
         * All properties of the provided object will be
         * copied into the prototype of all Module instances.
         *
         * @access  public
         * @param   Object  Template with properties to include.
         * @return  Object  Instance, fluid interface.
         */
        self.include = function(){
            var _include = function(obj){
                var included = obj.included;
                for(var i in obj){
                    if(obj.hasOwnProperty(i))
                        self.fn[i] = obj[i];
                }
                if(included) included.call(self.fn, self.fn);
            };

            var i = 0, t = arguments.length;
            for(;i<t;i++){
                _include(arguments[i]);
            }

            return self;
        };

        /**
         * Utility method to proxy function calls
         * with the proper scope.
         * Any extra parameters passed to it, will be
         * concatenated into the final call.
         *
         * @access public
         * @param   Function    Function to be proxied.
         * @return  Function    Wrapped function with scope set to self.
         */
        self.proxy = function(func){
            var a = _splice.call(arguments,1);
            var self = this;
            return function(){
                var a2 = _splice.call(arguments,0);
                return func.apply(self, a.concat(a2));
            };
        };

        //shortcuts.
        self.fn = self.prototype;
        self.fn.parent = parent; //TODO: We most likely do not need this, since we have now _super.
        self.fn.proxy  = self.proxy;

        // The class/parent name
        self.prototype.__name__  = self.__name__  = name;
        self.prototype.__class__ = self;

        //Store a reference by name in the provided namespace.
        _namespace[name] = self;

        /**
         * Hook to execute "extending" method on parent
         * after we create module. This allows for DRY
         * "extending" implementations.
         * @see  'the "extending" method allows to DRY extended' in
         *       specs.
         */
        if('extending' in self) self.extending();

        return self;
    };

    Module.__name__    = 'Module';
    Module.__version__ = '0.2.1';

    //TODO: Remove?!
    Module.decorator = function(implementation){
        var self = this;
        var Decorator = function(){};
        Decorator.prototype.decorate = function(){
            var i = 0,
                t = arguments.length;
            for(;i < t; i++){
                implementation( arguments[i], this);
            }
        };

        Decorator.prototype.owner = self;

        return new Decorator(self);
    };

    /**
     * Override a method, by default the source object
     * is the current modules prototype.
     * If the source object is not a Module, we create a _super
     * object and attach the original method.
     *
     *
     * @param  {Function}   src
     * @param  {Function}   method
     * @param  {Function} fn
     * @return {void}
     */
    Module.override = function(method, fn, src){
        src = src || this.prototype;
        if(!('_super' in src)){
            src._super = {};
            src._super[method] = src[method];
        }
        src[method] = fn;
    };

    /**
     * Utility method to clone an object.
     *
     * @access  public.
     * @param   object  Object to be cloned.
     * @return  object  Cloned object.
     */
    Module.clone = function(src){
        if (typeof src === 'function') return src;
        if (typeof src !== 'object') return src;
        var out = _isArray(src) ? [] : {};
        return _merge(out, src);
    };

    /*Module.merge = function(){
        console.log(_splice.call(arguments,0))
        return jQuery.extend.apply(jQuery, _splice.call(arguments,0));
    };*/

    /**
     * Scope in which the module will store all clases,
     * to prevent pollution of the global namespace.
     *
     * @access public
     * @param   object  scoped namespace for the module.
     * @return  object  scoped namespace for the module.
     */
    Module.namespace = function(ns){
        var namespace = ns || _namespace;
        if(!namespace.hasOwnProperty(_exportName))
            namespace[_exportName] = Module;
        return namespace;
    };

/*
    Module.prototype.init = function(){
        console.log('Peperone');
        return 'This is just a stub!';
    };*/

    return Module;
}));<|endoftext|>


<commit_before>import Obj from 'vood/obj/class';

export default Obj({
	////-----------------------------------------------------------------------------------------
	// In which package does requirejs use the templates
	templatePrefix: 'templates/',
	////-----------------------------------------------------------------------------------------
	// Jquery selector where the this.startPath controller and view are added
	entrance: '#app',
	////-----------------------------------------------------------------------------------------
	// Controller which is added at startup
	startPath: 'main/app',
	////-----------------------------------------------------------------------------------------
	// domnode-attribute for start uid node
	uidDomNode: 'script',
	////-----------------------------------------------------------------------------------------
	// domnode-attribute for start uid node
	uidAttrStart: 'data-begin',
	////-----------------------------------------------------------------------------------------
	// domnode-attribute for end uid node
	uidAttrEnd: 'data-end',
	////-----------------------------------------------------------------------------------------
	// Existing classes collection
	list: {},
	////-----------------------------------------------------------------------------------------
	// saves which views changed
	dirties: [],
	////-----------------------------------------------------------------------------------------
	// Decides which instances are asked for handling events
	eventSpaces: [ 'controllerHelper', 'helperHelper' ],
	////-----------------------------------------------------------------------------------------
	// JQuery events which the framework is listening for
	eventString: 'click submit change mouseover mouseout mousemove mouseup mousedown keyup keydown drag dragstart dragover mousewheel',
	////-----------------------------------------------------------------------------------------
	// maps e.g. keypresses to trigger shortevents for enter and escape-keys
	eventMap: {
		keyup: {
			keyCode: {
				13: 'enterkey',
				27: 'escapekey'
			}

		}
	},
	_meta: {
		contentSpace: false
	},
	////-----------------------------------------------------------------------------------------
	// adds dirtychecking to runloop and inserts first view this.startPath and starts document event listener
	init: function(){
		tempartCompiler.partial = this.partialHandling; // Thats how you configure tempart...
		if( this.dirtyHandling !== false ){
			this.addJob( {callback: this.dirtyChecking} );
		}
		this.checkValidity();
		this.insertApp();
		this.addEvents();
	},
	////-----------------------------------------------------------------------------------------
	// adds dirtychecking to runloop and inserts first view this.startPath and starts document event listener
	partialHandling: function( block, context, currentValues, dirties, path ) {
		// @TODO add uid to currentValues
		currentValues[ block.id ] = {path, uid: 1 + vood.controllerHelper.id};
		return vood.controllerHelper.create( path, context ).html;
	},
	////-----------------------------------------------------------------------------------------
	// creates instance of view
	create: function( path, opt ){
		return this.getEntity(path);
	},
	////-----------------------------------------------------------------------------------------
	// inserts first view to this.entrance
	insertApp: function(){
		var result = vood.controllerHelper.create( this.startPath, null, {} );
		var dom = $( this.entrance );
		if( dom.length === 1 ) {
			$( this.entrance ).replaceWith( result.html );
		} else {
			console.error( 'vood.viewHelper.entrance was not represented in dom properly', dom );
		}
	},
	////-----------------------------------------------------------------------------------------
	// gets class and returns instance
	getEntity: function( path ){
		if( !this.list[ path ]){
			console.log( 'View ' + path + ' does not exist' );
			vood.View( path, {_meta: {pseudo: true}} );
		}
		return _.cloneDeep( this.list[ path ] );
	},
	////-----------------------------------------------------------------------------------------
	// start uid
	// @TODO move this to the templating engine
	scriptStart: function( id, path ){
		return '<' +this.uidDomNode+ ' ' +this.uidAttrStart+ '="' +id+ '" data-template="' + path + '"></' +this.uidDomNode+ '>';
	},
	////-----------------------------------------------------------------------------------------
	// end uid
	scriptEnd: function( id ){
		return '<' +this.uidDomNode+ ' ' +this.uidAttrEnd+ '="' +id + '"></' +this.uidDomNode+ '>';
	},
	////-----------------------------------------------------------------------------------------
	// some basic checking if values are correct
	checkValidity: function(){
		if( !this.entrance ){
			throw "App entrance is not defined";
		}
	},
	////-----------------------------------------------------------------------------------------
	// checks which views are dirty, to asynchronoesly render them
	dirtyChecking: function(){
		if(vood.viewHelper.dirties) {
			// console.log(vood.viewHelper.dirties.length);
			for( var i = 0; i < vood.viewHelper.dirties.length; i++ ){
				var uid = vood.viewHelper.dirties[i];
				var controllers = vood.controllerHelper.search(uid);
				for( var controllerIndex = 0; controllerIndex < controllers.length; controllerIndex++ ){
					var controller = controllers[ controllerIndex ];
					controller.view._handleDirties();
				}
			}
			vood.viewHelper.dirties = [];
		}
	},
	////-----------------------------------------------------------------------------------------
	// iterates through all dom-nodes to the top and returns an array of controllers
	getUids: function( obj ){
		var result    = [];
		var excludes  = [];
		while( obj.length > 0 ){
			// Needed handling with prev() instead of siblings(), to keep the order of the uids
			var prevObj = obj.prev();
			while( prevObj.length > 0 ){
				var endUid = this.isUidEndObj( prevObj );
				var uid = this.isUidObj( prevObj );

				if( endUid ){
					excludes.push(endUid); // Needed to handle siblings, which do not surround the target
				} else if( uid && excludes.indexOf( uid ) === -1 ){
					result.push( uid );
				}
				prevObj = prevObj.prev();
			}
			obj = obj.parent();
		}

		return result;
	},
	////-----------------------------------------------------------------------------------------
	// checks if dom-node is an uid-obj
	isUidObj: function( obj ){
		if( obj.is( this.uidDomNode + '[' + this.uidAttrStart + ']' )) {
			return obj.attr( this.uidAttrStart );
		}
	},
	////-----------------------------------------------------------------------------------------
	// checks if dom-node is an uid-end-obj
	isUidEndObj: function( obj ){
		if( obj.is( this.uidDomNode + '[' + this.uidAttrEnd + ']' )) {
			return obj.attr( this.uidAttrEnd );
		}
	},
	////-----------------------------------------------------------------------------------------
	// adds all dom events which the framework ist listening for
	addEvents: function() {
		$( 'body' ).on( this.eventString, function( evt ) {
			return vood.viewHelper.handleEvent( evt );
		});
		$( window ).on( 'hashchange', function() {
			trhis.trigger( 'hashchange', location.hash );
		});
	},
	////-----------------------------------------------------------------------------------------
	// wrapper for triggering events, selects corresponding parent-controllers
	handleEvent: function( evt ){
		var uids = this.getUids( $(evt.target));
		this.triggerExtra( evt );
		this.updateData( evt );
		return this.triggerEvent( evt.type, evt, {controllers: uids, pseudo: false} );
	},
	////-----------------------------------------------------------------------------------------
	// way binding from dom
	updateData: function( evt ) {
		if( evt.type == 'keyup' || evt.type == 'keydown' ){
			var identifier = evt.target.getAttribute('tempartstart');
			if( identifier){
				var pos = identifier.indexOf( '-' );
				var uid = identifier.slice( 0, pos );
				var blockId = identifier.slice( pos + 1, identifier.length );
				var controllers = vood.search( uid );
				if( controllers.length === 1 ){
					var controller = controllers[ 0 ];
					controller.view._updateCurrent(blockId, 'value', evt.target.value);
				} else {
					throw 'Getting the correct controller failed somehow';
				}
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// handles all the events
	// @TODO move this to the eventhandler
	triggerEvent: function( type, evt, opt ){
		if( !evt ) evt = {};
		if( type.search(':') != -1) throw 'events are not allowed to have a : inside' ;
		opt = this.prepareEventOpt( opt );
		var result = [];
		for( var i = 0; i < opt.controllers.length; i++){
			var uid = opt.controllers[i];
			for( var spaceIndex = 0; spaceIndex < this.eventSpaces.length; spaceIndex++ ) {
				var controllers = vood[ this.eventSpaces[ spaceIndex] ].search( uid );
				for( var controllerIndex = 0; controllerIndex < controllers.length; controllerIndex++ ){
					var value = null;
					if(controllers[ controllerIndex ].view) {
						value = controllers[ controllerIndex ].view._checkForEvent( type, evt, opt );
					} else {
						value = controllers[ controllerIndex ]._checkForEvent( type, evt, opt );
					}
					if( value.found ) result.push(value.result);
					if( !opt.pseudo && evt.propagation === false ) break;
				}
				if( !opt.pseudo && evt.propagation === false ) break;
			}
			if( !opt.pseudo && evt.propagation === false ) break;
		}

		// Warn only when it either was a clickevent, or an pseudoevent
		if( !result.length && (opt.pseudo || (!opt.pseudo && evt.type == 'click' ))) {
			console.warn( 'There was no eventdefinition found ' + type );
		}
		return result;
	},
	////-----------------------------------------------------------------------------------------
	// triggers eventmaps like keypresses to shortevents like "enterkey" and escapekey
	// @TODO make this deprecated
	triggerExtra: function( evt ){
		if( this.eventMap[ evt.type ] ){
			for( var index in this.eventMap[ evt.type ] ) {
				var eventValue = evt[ index ];
				if( eventValue ) {
					var mapValue = this.eventMap[ evt.type ][ index ][ eventValue ];
					if( mapValue ){
						this.trigger( mapValue, _.clone( evt ));
					}
				}
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// Checks variables inside the eventdefition
	// @TODO make this deprecated, and move it to the view-class
	checkEventMatch: function( definition, type, opt ) {
		var reserved = ':';
		if(definition == type) {
			return true;
		} else {
			var pos  = definition.search(reserved);
			var diff = 0;
			var params = {};
			while( pos != -1 ){
				var leftoverDefinition = definition.substring( pos + 1, definition.length );
				var leftover           = type.substring( pos, type.length );
				var lengthDefinition   = leftoverDefinition.search( /\W/ );
				var length             = leftover.search( /\W/ );
				if( lengthDefinition === -1 ) lengthDefinition = leftoverDefinition.length;
				if( length === -1 ) length = leftover.length;
				var key                = leftoverDefinition.substring( 0, lengthDefinition);
				var value              = leftover.substring( 0, length);
				var reg                = new RegExp(':'+key);
				definition             = definition.replace(reg, value);
				pos                    = definition.search(reserved);
				params[ key ] = value;

			}

			if(definition === type) {
				for( var i in params ) {
					opt[ i ] = params[ i ];
				}
				return true;
			}

		}
	},
	////-----------------------------------------------------------------------------------------
	// recusively goes upside in the dom, to collect dataattributes
	getAttributes: function( target, result ){
		if(!result) result = {};

		if(target) {
			var attributes = target[0].attributes;

			if(result.id === undefined && target.attr('id') && attributes['data-id'] === undefined) {
				result.id = target.attr('id');
			}

			var excludes = ['ember-extension'];

			for (var index in attributes) {
				if (attributes.hasOwnProperty(index)) {
					var name  = attributes[index].name,
						value = attributes[index].value;
					if(name && name.search('data-') === 0) {
						var key = name.replace(/data-/, '');
						if(result[key] === undefined && excludes.indexOf(key) == -1) { // SHould only overwrite when not set
							result[key] = value || null;
						}
					}
				}
			}
			var parent = target.parent();
			if(parent.length > 0) {
				result = this.getAttributes(parent, result);
			}
		}
		return result;
	},
	////-----------------------------------------------------------------------------------------
	// makes some default values for events
	prepareEventOpt: function( opt ){
		if( !opt ) opt = {};
		if( opt.pseudo !== false ) opt.pseudo = true;
		if( !opt.controllers ) opt.controllers = [ '*' ];
		return opt;
	}
});<commit_msg>[viewhelper] an enter does not change its attribute value<commit_after>import Obj from 'vood/obj/class';

export default Obj({
	////-----------------------------------------------------------------------------------------
	// In which package does requirejs use the templates
	templatePrefix: 'templates/',
	////-----------------------------------------------------------------------------------------
	// Jquery selector where the this.startPath controller and view are added
	entrance: '#app',
	////-----------------------------------------------------------------------------------------
	// Controller which is added at startup
	startPath: 'main/app',
	////-----------------------------------------------------------------------------------------
	// domnode-attribute for start uid node
	uidDomNode: 'script',
	////-----------------------------------------------------------------------------------------
	// domnode-attribute for start uid node
	uidAttrStart: 'data-begin',
	////-----------------------------------------------------------------------------------------
	// domnode-attribute for end uid node
	uidAttrEnd: 'data-end',
	////-----------------------------------------------------------------------------------------
	// Existing classes collection
	list: {},
	////-----------------------------------------------------------------------------------------
	// saves which views changed
	dirties: [],
	////-----------------------------------------------------------------------------------------
	// Decides which instances are asked for handling events
	eventSpaces: [ 'controllerHelper', 'helperHelper' ],
	////-----------------------------------------------------------------------------------------
	// JQuery events which the framework is listening for
	eventString: 'click submit change mouseover mouseout mousemove mouseup mousedown keyup keydown drag dragstart dragover mousewheel',
	////-----------------------------------------------------------------------------------------
	// maps e.g. keypresses to trigger shortevents for enter and escape-keys
	eventMap: {
		keyup: {
			keyCode: {
				13: 'enterkey',
				27: 'escapekey'
			}

		}
	},
	_meta: {
		contentSpace: false
	},
	////-----------------------------------------------------------------------------------------
	// adds dirtychecking to runloop and inserts first view this.startPath and starts document event listener
	init: function(){
		tempartCompiler.partial = this.partialHandling; // Thats how you configure tempart...
		if( this.dirtyHandling !== false ){
			this.addJob( {callback: this.dirtyChecking} );
		}
		this.checkValidity();
		this.insertApp();
		this.addEvents();
	},
	////-----------------------------------------------------------------------------------------
	// adds dirtychecking to runloop and inserts first view this.startPath and starts document event listener
	partialHandling: function( block, context, currentValues, dirties, path ) {
		// @TODO add uid to currentValues
		currentValues[ block.id ] = {path, uid: 1 + vood.controllerHelper.id};
		return vood.controllerHelper.create( path, context ).html;
	},
	////-----------------------------------------------------------------------------------------
	// creates instance of view
	create: function( path, opt ){
		return this.getEntity(path);
	},
	////-----------------------------------------------------------------------------------------
	// inserts first view to this.entrance
	insertApp: function(){
		var result = vood.controllerHelper.create( this.startPath, null, {} );
		var dom = $( this.entrance );
		if( dom.length === 1 ) {
			$( this.entrance ).replaceWith( result.html );
		} else {
			console.error( 'vood.viewHelper.entrance was not represented in dom properly', dom );
		}
	},
	////-----------------------------------------------------------------------------------------
	// gets class and returns instance
	getEntity: function( path ){
		if( !this.list[ path ]){
			console.log( 'View ' + path + ' does not exist' );
			vood.View( path, {_meta: {pseudo: true}} );
		}
		return _.cloneDeep( this.list[ path ] );
	},
	////-----------------------------------------------------------------------------------------
	// start uid
	// @TODO move this to the templating engine
	scriptStart: function( id, path ){
		return '<' +this.uidDomNode+ ' ' +this.uidAttrStart+ '="' +id+ '" data-template="' + path + '"></' +this.uidDomNode+ '>';
	},
	////-----------------------------------------------------------------------------------------
	// end uid
	scriptEnd: function( id ){
		return '<' +this.uidDomNode+ ' ' +this.uidAttrEnd+ '="' +id + '"></' +this.uidDomNode+ '>';
	},
	////-----------------------------------------------------------------------------------------
	// some basic checking if values are correct
	checkValidity: function(){
		if( !this.entrance ){
			throw "App entrance is not defined";
		}
	},
	////-----------------------------------------------------------------------------------------
	// checks which views are dirty, to asynchronoesly render them
	dirtyChecking: function(){
		if(vood.viewHelper.dirties) {
			// console.log(vood.viewHelper.dirties.length);
			for( var i = 0; i < vood.viewHelper.dirties.length; i++ ){
				var uid = vood.viewHelper.dirties[i];
				var controllers = vood.controllerHelper.search(uid);
				for( var controllerIndex = 0; controllerIndex < controllers.length; controllerIndex++ ){
					var controller = controllers[ controllerIndex ];
					controller.view._handleDirties();
				}
			}
			vood.viewHelper.dirties = [];
		}
	},
	////-----------------------------------------------------------------------------------------
	// iterates through all dom-nodes to the top and returns an array of controllers
	getUids: function( obj ){
		var result    = [];
		var excludes  = [];
		while( obj.length > 0 ){
			// Needed handling with prev() instead of siblings(), to keep the order of the uids
			var prevObj = obj.prev();
			while( prevObj.length > 0 ){
				var endUid = this.isUidEndObj( prevObj );
				var uid = this.isUidObj( prevObj );

				if( endUid ){
					excludes.push(endUid); // Needed to handle siblings, which do not surround the target
				} else if( uid && excludes.indexOf( uid ) === -1 ){
					result.push( uid );
				}
				prevObj = prevObj.prev();
			}
			obj = obj.parent();
		}

		return result;
	},
	////-----------------------------------------------------------------------------------------
	// checks if dom-node is an uid-obj
	isUidObj: function( obj ){
		if( obj.is( this.uidDomNode + '[' + this.uidAttrStart + ']' )) {
			return obj.attr( this.uidAttrStart );
		}
	},
	////-----------------------------------------------------------------------------------------
	// checks if dom-node is an uid-end-obj
	isUidEndObj: function( obj ){
		if( obj.is( this.uidDomNode + '[' + this.uidAttrEnd + ']' )) {
			return obj.attr( this.uidAttrEnd );
		}
	},
	////-----------------------------------------------------------------------------------------
	// adds all dom events which the framework ist listening for
	addEvents: function() {
		$( 'body' ).on( this.eventString, function( evt ) {
			return vood.viewHelper.handleEvent( evt );
		});
		$( window ).on( 'hashchange', function() {
			trhis.trigger( 'hashchange', location.hash );
		});
	},
	////-----------------------------------------------------------------------------------------
	// wrapper for triggering events, selects corresponding parent-controllers
	handleEvent: function( evt ){
		var uids = this.getUids( $(evt.target));
		this.triggerExtra( evt );
		this.updateData( evt );
		return this.triggerEvent( evt.type, evt, {controllers: uids, pseudo: false} );
	},
	////-----------------------------------------------------------------------------------------
	// way binding from dom
	updateData: function( evt ) {
		if( evt.type == 'keyup' || evt.type == 'keydown' ){
			var identifier = evt.target.getAttribute('tempartstart');
			if( identifier && evt.keyCode !== 13 ){ // Enter button don't bring changes into the values
				var pos = identifier.indexOf( '-' );
				var uid = identifier.slice( 0, pos );
				var blockId = identifier.slice( pos + 1, identifier.length );
				var controllers = vood.search( uid );
				if( controllers.length === 1 ){
					var controller = controllers[ 0 ];
					controller.view._updateCurrent(blockId, 'value', evt.target.value);
				} else {
					throw 'Getting the correct controller failed somehow';
				}
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// handles all the events
	// @TODO move this to the eventhandler
	triggerEvent: function( type, evt, opt ){
		if( !evt ) evt = {};
		if( type.search(':') != -1) throw 'events are not allowed to have a : inside' ;
		opt = this.prepareEventOpt( opt );
		var result = [];
		for( var i = 0; i < opt.controllers.length; i++){
			var uid = opt.controllers[i];
			for( var spaceIndex = 0; spaceIndex < this.eventSpaces.length; spaceIndex++ ) {
				var controllers = vood[ this.eventSpaces[ spaceIndex] ].search( uid );
				for( var controllerIndex = 0; controllerIndex < controllers.length; controllerIndex++ ){
					var value = null;
					if(controllers[ controllerIndex ].view) {
						value = controllers[ controllerIndex ].view._checkForEvent( type, evt, opt );
					} else {
						value = controllers[ controllerIndex ]._checkForEvent( type, evt, opt );
					}
					if( value.found ) result.push(value.result);
					if( !opt.pseudo && evt.propagation === false ) break;
				}
				if( !opt.pseudo && evt.propagation === false ) break;
			}
			if( !opt.pseudo && evt.propagation === false ) break;
		}

		// Warn only when it either was a clickevent, or an pseudoevent
		if( !result.length && (opt.pseudo || (!opt.pseudo && evt.type == 'click' ))) {
			console.warn( 'There was no eventdefinition found ' + type );
		}
		return result;
	},
	////-----------------------------------------------------------------------------------------
	// triggers eventmaps like keypresses to shortevents like "enterkey" and escapekey
	// @TODO make this deprecated
	triggerExtra: function( evt ){
		if( this.eventMap[ evt.type ] ){
			for( var index in this.eventMap[ evt.type ] ) {
				var eventValue = evt[ index ];
				if( eventValue ) {
					var mapValue = this.eventMap[ evt.type ][ index ][ eventValue ];
					if( mapValue ){
						this.trigger( mapValue, _.clone( evt ));
					}
				}
			}
		}
	},
	////-----------------------------------------------------------------------------------------
	// Checks variables inside the eventdefition
	// @TODO make this deprecated, and move it to the view-class
	checkEventMatch: function( definition, type, opt ) {
		var reserved = ':';
		if(definition == type) {
			return true;
		} else {
			var pos  = definition.search(reserved);
			var diff = 0;
			var params = {};
			while( pos != -1 ){
				var leftoverDefinition = definition.substring( pos + 1, definition.length );
				var leftover           = type.substring( pos, type.length );
				var lengthDefinition   = leftoverDefinition.search( /\W/ );
				var length             = leftover.search( /\W/ );
				if( lengthDefinition === -1 ) lengthDefinition = leftoverDefinition.length;
				if( length === -1 ) length = leftover.length;
				var key                = leftoverDefinition.substring( 0, lengthDefinition);
				var value              = leftover.substring( 0, length);
				var reg                = new RegExp(':'+key);
				definition             = definition.replace(reg, value);
				pos                    = definition.search(reserved);
				params[ key ] = value;

			}

			if(definition === type) {
				for( var i in params ) {
					opt[ i ] = params[ i ];
				}
				return true;
			}

		}
	},
	////-----------------------------------------------------------------------------------------
	// recusively goes upside in the dom, to collect dataattributes
	getAttributes: function( target, result ){
		if(!result) result = {};

		if(target) {
			var attributes = target[0].attributes;

			if(result.id === undefined && target.attr('id') && attributes['data-id'] === undefined) {
				result.id = target.attr('id');
			}

			var excludes = ['ember-extension'];

			for (var index in attributes) {
				if (attributes.hasOwnProperty(index)) {
					var name  = attributes[index].name,
						value = attributes[index].value;
					if(name && name.search('data-') === 0) {
						var key = name.replace(/data-/, '');
						if(result[key] === undefined && excludes.indexOf(key) == -1) { // SHould only overwrite when not set
							result[key] = value || null;
						}
					}
				}
			}
			var parent = target.parent();
			if(parent.length > 0) {
				result = this.getAttributes(parent, result);
			}
		}
		return result;
	},
	////-----------------------------------------------------------------------------------------
	// makes some default values for events
	prepareEventOpt: function( opt ){
		if( !opt ) opt = {};
		if( opt.pseudo !== false ) opt.pseudo = true;
		if( !opt.controllers ) opt.controllers = [ '*' ];
		return opt;
	}
});<|endoftext|>


<commit_before>import {Parser} from 'jison'
import Lexer from './lexer'

var statement;

function list(item) {
    return [
        [`${item} ${item}s`, "$$ = [$1].concat($2);" ],
        ["", "$$ = [];" ],
    ]
}

export var parser = new Parser({

    "operators": [
        ["left", "+", "-"],
        ["left", "*", "/"],
        ["left", "^"],
        ["left", "(", ".", "["],
        ["left", "UNARY"]
    ],
    "start": "file",
    "bnf": {
        "file": [
            ["blocks EOF", "return $1;"],
            ["NL blocks EOF", "return $2;"],
        ],
        "blocks": list('block'),
        "block": [
            ["import { names } from STRING NL",
                "$$ = ['import_names', $3, $6];"],
            ["import IDENT from STRING NL",
                "$$ = ['import_default', $2, $4];"],
            ["style : NL INDENT rules DEDENT", "$$ = ['style', $5];"],
            ["style : NL", "$$ = ['style', []]"],
            ["view IDENT ( args ) : NL stmtblock", "$$ = ['view', $2, $4, $8]"],
        ],
        "names": [
            ["IDENT , names", "$$ = [$1].concat($3);"],
            ["IDENT", "$$ = [$1];"],
        ],
        // CSS
        "rules": [
            ["rule rules", "$$ = [$1].concat($2);"],
            ["", "$$ = [];"],
        ],
        "rule": [
            ["selectors NL INDENT properties DEDENT", "$$ = ['rule', $1, $4]"],
            ["selectors NL", "$$ = ['rule', $1, []]"],
        ],
        "selectors": [
            ["selector", "$$ = [$1]"],
            ["selector , selectors", "$$ = [$1].concat($3)"],
        ],
        "selector": [
            ["IDENT_TOKEN", "$$ = $1;"],
            ["css_classes", "$$ = $1;"],
            ["IDENT_TOKEN css_classes", "$$ = $1 + $2;"],
        ],
        "css_classes": [
            [". IDENT_TOKEN", "$$ = '.' +$2"],
            [". IDENT_TOKEN css_classes", "$$ = '.' + $2 + $3"],
            [": IDENT_TOKEN css_classes", "$$ = ':' + $2 + $3"],
        ],
        "properties": [
            ["property properties", "$$ = [$1].concat($2);"],
            ["", "$$ = [];"],
        ],
        "property": [
            ["IDENT_TOKEN : property_value", "$$ = ['property', $1, $3]"],
        ],
        "property_value": [
            ["NL", "$$ = '';"],
            ["NL INDENT css_lines DEDENT", "$$ = $3"],
            ["css_value NL", "$$ = $1;"],
            ["css_value NL INDENT css_lines DEDENT", "$$ = $1 + ' ' + $4"],
        ],
        "css_lines": [
            ["css_value NL", "$$ = $1"],
            ["css_value NL css_lines", "$$ = $1 + ' ' + $3"],
        ],
        "css_value": [
            ["css_item", "$$ = $1;"],
            ["css_item css_value", "$$ = $1 + ' ' + $2"],
        ],
        "css_item": [
            ["IDENT_TOKEN", "$$ = $1"],
            ["HASH_TOKEN", "$$ = $1"],
            ["URL", "$$ = $1"],
            ["DIMENSION", "$$ = $1"],
            ["NUMBER", "$$ = $1"],
        ],
        // HTML
        "args": [
            ["IDENT , args", "$$ = [$1].concat($3);"],
            ["IDENT", "$$ = [$1];"],
            ["", "$$ = [];"],
        ],
        "stmtblock": [
            ["INDENT statements DEDENT", "$$ = $2"],
            ["", "$$ = []"],
        ],
        "statements": list('statement'),
        "elstatements": list('elstatement'),
        "statement": [
            ["< TAG_NAME classes attributes > NL",
                "$$ = ['element', $2, $3, $4, []];" ],
            ["< TAG_NAME classes attributes > NL INDENT elstatements DEDENT",
                "$$ = ['element', $2, $3, $4, $8];" ],
            ["store STORE = e NL", "$$ = ['store', $2, $4]"],
            ["let IDENT = e NL", "$$ = ['assign', $2, $4]"],
            ["if e : NL stmtblock elifblocks", "$$ = ['if', [$2, $5], $6]"],
            ["if e : NL stmtblock elifblocks elseblock",
                "$$ = ['if', [$2, $5], $6, $7]"],
            ["for lval of e : NL stmtblock", "$$ = ['for', $2, $4, $2, $7]"],
            ["for lval of e key e : NL stmtblock",
                "$$ = ['for', $2, $4, $6, $9]"],
            ["e NL", "$$ = ['expression', $1];" ],
        ],
        "classes": list('classe'),
        "classe": [
            [". TAG_NAME", "$$ = [$2];"],
            [". TAG_NAME ? ( e )", "$$ = [$2, $5];"],
        ],
        "elifblocks": list('elifblock'),
        "elifblock": [
            ["elif e : NL stmtblock", "$$ = [$2, $5]"],
        ],
        "elseblock": [
            ["else : NL stmtblock", "$$ = $4;"],
        ],
        "elstatement": [
            ["statement", "$$ = $1;"],
            // TODO(tailhook) support other targets than stores
            ["link { names } e -> STORE NL",
                "$$ = ['link', $3, $5, ['store', $7]];"],
        ],
        "attributes": list('attribute'),
        "attribute": [
            ["TAG_NAME = attrvalue", "$$ = [$1, $3];"],
            ["TAG_NAME", "$$ = [$1];"],
        ],
        "attrvalue": [
            ["NUMBER", "$$ = ['number', $1];"],
            ["STRING", "$$ = ['string', $1];"],
            ["TAG_NAME", "$$ = ['name', $1];"],
            ["STORE", "$$ = ['store', $1];"],
            ["attrvalue . TAG_NAME", "$$ = ['attr', $1, $3];"],
        ],
        "lval": [
            ["IDENT", "$$ = ['name', $1]"],
        ],
        "comma_separated": [
            ["e , comma_separated", "$$ = [$1].concat($3);"],
            ["e", "$$ = [$1];"],
            ["", "$$ = [];"],
        ],
        "e" :[
              [ "e + e",   "$$ = ['binop', '+', $1, $3];" ],
              [ "e - e",   "$$ = ['binop', '-', $1, $3];" ],
              [ "e * e",   "$$ = ['binop', '*', $1, $3];" ],
              [ "e / e",   "$$ = ['binop', '/', $1, $3];" ],
              [ "e ^ e",   "$$ = ['pow', $1, $3];" ],
              [ "- e",     "$$ = ['minus', $2];", {"prec": "UNARY"} ],
              [ "+ e",     "$$ = ['plus', $2];", {"prec": "UNARY"} ],
              [ "( e )",   "$$ = $2;" ],
              [ "e ( comma_separated )", "$$ = ['call', $1, $3];" ],
              [ "e [ e ]", "$$ = ['index', $1, $3];" ],
              [ "e . IDENT", "$$ = ['attr', $1, $3];" ],
              [ "[ comma_separated ]",       "$$ = ['list', $2];" ],
              [ "NUMBER",  "$$ = ['number', $1];" ],
              [ "STRING",  "$$ = ['string', $1];" ],
              [ "IDENT",  "$$ = ['name', $1];" ],
              [ "STORE",  "$$ = ['store', $1];" ],
        ],
    }
})
parser.lexer = new Lexer()
<commit_msg>Better support of css pseudo-elements<commit_after>import {Parser} from 'jison'
import Lexer from './lexer'

var statement;

function list(item) {
    return [
        [`${item} ${item}s`, "$$ = [$1].concat($2);" ],
        ["", "$$ = [];" ],
    ]
}

export var parser = new Parser({

    "operators": [
        ["left", "+", "-"],
        ["left", "*", "/"],
        ["left", "^"],
        ["left", "(", ".", "["],
        ["left", "UNARY"]
    ],
    "start": "file",
    "bnf": {
        "file": [
            ["blocks EOF", "return $1;"],
            ["NL blocks EOF", "return $2;"],
        ],
        "blocks": list('block'),
        "block": [
            ["import { names } from STRING NL",
                "$$ = ['import_names', $3, $6];"],
            ["import IDENT from STRING NL",
                "$$ = ['import_default', $2, $4];"],
            ["style : NL INDENT rules DEDENT", "$$ = ['style', $5];"],
            ["style : NL", "$$ = ['style', []]"],
            ["view IDENT ( args ) : NL stmtblock", "$$ = ['view', $2, $4, $8]"],
        ],
        "names": [
            ["IDENT , names", "$$ = [$1].concat($3);"],
            ["IDENT", "$$ = [$1];"],
        ],
        // CSS
        "rules": [
            ["rule rules", "$$ = [$1].concat($2);"],
            ["", "$$ = [];"],
        ],
        "rule": [
            ["selectors NL INDENT properties DEDENT", "$$ = ['rule', $1, $4]"],
            ["selectors NL", "$$ = ['rule', $1, []]"],
        ],
        "selectors": [
            ["selector", "$$ = [$1]"],
            ["selector , selectors", "$$ = [$1].concat($3)"],
        ],
        "selector": [
            ["IDENT_TOKEN", "$$ = $1;"],
            ["css_classes", "$$ = $1;"],
            ["IDENT_TOKEN css_classes", "$$ = $1 + $2;"],
        ],
        "css_classes": [
            [". IDENT_TOKEN", "$$ = '.' +$2"],
            [": IDENT_TOKEN", "$$ = ':' +$2"],
            [". IDENT_TOKEN css_classes", "$$ = '.' + $2 + $3"],
            [": IDENT_TOKEN css_classes", "$$ = ':' + $2 + $3"],
        ],
        "properties": [
            ["property properties", "$$ = [$1].concat($2);"],
            ["", "$$ = [];"],
        ],
        "property": [
            ["IDENT_TOKEN : property_value", "$$ = ['property', $1, $3]"],
        ],
        "property_value": [
            ["NL", "$$ = '';"],
            ["NL INDENT css_lines DEDENT", "$$ = $3"],
            ["css_value NL", "$$ = $1;"],
            ["css_value NL INDENT css_lines DEDENT", "$$ = $1 + ' ' + $4"],
        ],
        "css_lines": [
            ["css_value NL", "$$ = $1"],
            ["css_value NL css_lines", "$$ = $1 + ' ' + $3"],
        ],
        "css_value": [
            ["css_item", "$$ = $1;"],
            ["css_item css_value", "$$ = $1 + ' ' + $2"],
        ],
        "css_item": [
            ["IDENT_TOKEN", "$$ = $1"],
            ["HASH_TOKEN", "$$ = $1"],
            ["URL", "$$ = $1"],
            ["DIMENSION", "$$ = $1"],
            ["NUMBER", "$$ = $1"],
        ],
        // HTML
        "args": [
            ["IDENT , args", "$$ = [$1].concat($3);"],
            ["IDENT", "$$ = [$1];"],
            ["", "$$ = [];"],
        ],
        "stmtblock": [
            ["INDENT statements DEDENT", "$$ = $2"],
            ["", "$$ = []"],
        ],
        "statements": list('statement'),
        "elstatements": list('elstatement'),
        "statement": [
            ["< TAG_NAME classes attributes > NL",
                "$$ = ['element', $2, $3, $4, []];" ],
            ["< TAG_NAME classes attributes > NL INDENT elstatements DEDENT",
                "$$ = ['element', $2, $3, $4, $8];" ],
            ["store STORE = e NL", "$$ = ['store', $2, $4]"],
            ["let IDENT = e NL", "$$ = ['assign', $2, $4]"],
            ["if e : NL stmtblock elifblocks", "$$ = ['if', [$2, $5], $6]"],
            ["if e : NL stmtblock elifblocks elseblock",
                "$$ = ['if', [$2, $5], $6, $7]"],
            ["for lval of e : NL stmtblock", "$$ = ['for', $2, $4, $2, $7]"],
            ["for lval of e key e : NL stmtblock",
                "$$ = ['for', $2, $4, $6, $9]"],
            ["e NL", "$$ = ['expression', $1];" ],
        ],
        "classes": list('classe'),
        "classe": [
            [". TAG_NAME", "$$ = [$2];"],
            [". TAG_NAME ? ( e )", "$$ = [$2, $5];"],
        ],
        "elifblocks": list('elifblock'),
        "elifblock": [
            ["elif e : NL stmtblock", "$$ = [$2, $5]"],
        ],
        "elseblock": [
            ["else : NL stmtblock", "$$ = $4;"],
        ],
        "elstatement": [
            ["statement", "$$ = $1;"],
            // TODO(tailhook) support other targets than stores
            ["link { names } e -> STORE NL",
                "$$ = ['link', $3, $5, ['store', $7]];"],
        ],
        "attributes": list('attribute'),
        "attribute": [
            ["TAG_NAME = attrvalue", "$$ = [$1, $3];"],
            ["TAG_NAME", "$$ = [$1];"],
        ],
        "attrvalue": [
            ["NUMBER", "$$ = ['number', $1];"],
            ["STRING", "$$ = ['string', $1];"],
            ["TAG_NAME", "$$ = ['name', $1];"],
            ["STORE", "$$ = ['store', $1];"],
            ["attrvalue . TAG_NAME", "$$ = ['attr', $1, $3];"],
        ],
        "lval": [
            ["IDENT", "$$ = ['name', $1]"],
        ],
        "comma_separated": [
            ["e , comma_separated", "$$ = [$1].concat($3);"],
            ["e", "$$ = [$1];"],
            ["", "$$ = [];"],
        ],
        "e" :[
              [ "e + e",   "$$ = ['binop', '+', $1, $3];" ],
              [ "e - e",   "$$ = ['binop', '-', $1, $3];" ],
              [ "e * e",   "$$ = ['binop', '*', $1, $3];" ],
              [ "e / e",   "$$ = ['binop', '/', $1, $3];" ],
              [ "e ^ e",   "$$ = ['pow', $1, $3];" ],
              [ "- e",     "$$ = ['minus', $2];", {"prec": "UNARY"} ],
              [ "+ e",     "$$ = ['plus', $2];", {"prec": "UNARY"} ],
              [ "( e )",   "$$ = $2;" ],
              [ "e ( comma_separated )", "$$ = ['call', $1, $3];" ],
              [ "e [ e ]", "$$ = ['index', $1, $3];" ],
              [ "e . IDENT", "$$ = ['attr', $1, $3];" ],
              [ "[ comma_separated ]",       "$$ = ['list', $2];" ],
              [ "NUMBER",  "$$ = ['number', $1];" ],
              [ "STRING",  "$$ = ['string', $1];" ],
              [ "IDENT",  "$$ = ['name', $1];" ],
              [ "STORE",  "$$ = ['store', $1];" ],
        ],
    }
})
parser.lexer = new Lexer()
<|endoftext|>


<commit_before>//todo: delete trigger after it runs - there is a max for the script

/**
 * Copy folders and files from source to destination.
 * Get parameters from userProperties,
 * Loop until time runs out,
 * then call timeout methods, saveProperties and createTrigger.
 */
function copy() {
    // CONSTANTS
    var MAX_RUNNING_TIME = 5.7 * 60 * 1000;   // 5.7 minutes in milliseconds
    var START_TIME = (new Date()).getTime();

    var timeIsUp,       // {boolean} true if max execution time is reached while executing script
        permissions,    // {boolean} true if sharing permissions should be copied from source to destination
        currTime,       // {number} integer representing current time in milliseconds
        copyObject,     // {object} all the files and folders to copy
        srcId,          // {string} identification for top level source folder
        destId,         // {string} identification for top level destination folder
        ss,             // {object} instance of Sheet class
        properties,     // {object} properties of current run
        query,          // {string} query to generate Files list
        files,          // {object} list of files within Drive folder
        item,           // {object} metadata of child item from current iteration
        currFolder,     // {object} metadata of folder whose children are currently being processed
        newfile,        // {Object} JSON metadata for newly created folder or file
        timeIsUp = false; // {boolean}

    properties = loadProperties();

    // get current children, or if none exist, query next folder from properties.remaining
    // todo: in real instances, currChildre.items could exist but still have length = 0, in which case I would also want to skip
    if ( properties.currChildren.items && properties.currChildren.items.length > 0) {

        processFiles(properties.currChildren.items)

    }

    // when currChildren is complete, move on to other queries from properties.remaining

    while ( properties.remaining.length > 0 && !timeIsUp) {

        currFolder = properties.remaining.shift();
        query = '"' + currFolder + '" in parents and trashed = false';
        // Note: pageToken will only be generated IF there are results on the next "page".  So I always want to test for it, but if it isn't present, then that's ok.  However, it can sort of be like my "continuationToken", maybe

        do {

            // get files
            files = getFiles(query);

            // loop through and process
            if (files.items && files.items.length > 0) {

                processFiles(files.items);

            } else {

                Logger.log('No children found.');
                // todo: get next folder from properties.remaining
            }

            // get next page token to continue iteration
            properties.pageToken = files.nextPageToken;

        } while (properties.pageToken && !timeIsUp);

    }

    if ( timeIsUp ) {
        saveState();
    }

    return;

    /**
     * Loops through array of files.items
     * and sends to the appropriate copy function (insertFolder or copyFile)
     *
     * @param {array} items the list of files over which to iterate
     */
    function processFiles(items) {
        while ( files.items.length > 0 && !timeIsUp ) {
            item = files.items.pop();
            currTime = (new Date()).getTime();
            timeIsUp = (currTime - START_TIME >= MAX_RUNNING_TIME);

            if ( item.mimeType == "application/vnd.google-apps.folder") {
                newfile = insertFolder(item);
            } else {
                newfile = copyFile(folders.items[i]);
            }

            if (newfile.id) {
                // sheet.getRange(row, column, numRows, numColumns)
                ss.getRange(ss.getLastRow(), 1, 1, 3).setValues([
                    "Copied",
                    newfile.id,
                    newfile.title,
                    newfile.defaultOpenWithLink
                ]);

            } else {
                // newfile is error message
                ss.getRange(ss.getLastRow(), 1, 1, 2).setValue([
                    "Error",
                    newfile
                ]);
            }

        }
        return;
    }

    /**
     * Try to insert folder with information from src file.
     * Success:
     *   1. Add key/value to properties.map with src/dest folder ID pair
     *   2. Add dest folder ID to properties.remaining array
     *   3. Add note to Logger spreadsheet with destination folder ID
     * Failure:
     *   1. Log failure in spreadsheet with src ID
     *
     * @pararm {Object} file File Resource with metadata from source folder
     */
    function insertFolder(file) {

        try {
            var r = Drive.Files.insert({
                "description": file.description,
                "title": file.title,
                "parents": [
                    {
                        "kind": "drive#fileLink",
                        "id": properties.map[file.parents[0].id]
                    }
                ],
                "mimeType": "application/vnd.google-apps.folder"
            });

            properties.remaining.push(r.id);

            return;
        }

        catch(err) {
            Logger.log(err.message);
            return err.message;
        }
    }

    /**
     * Try to copy file to destination parent.
     * Success:
     *   1. Log success in spreadsheet with file ID
     * Failure:
     *   1. Log error in spreadsheet with source ID
     *
     * @param {Object} file File Resource with metadata from source file
     */
    function copyFile(file) {
        try {
            var r = Drive.Files.copy(
                {
                "title": file.title,
                "parents": [
                    {
                        "kind": "drive#fileLink",
                        "id": properties.map[file.parents[0].id]
                    }
                ]
                },
                file.id
            );
            return r;
        }

        catch(err) {
            Logger.log(err.message);
            return err;
        }
    }

    /**
     * Gets files from query and returns fileList with metadata
     *
     * @param {string} query the query to select files from the Drive
     * @return {object} fileList object where fileList.items is an array of children files
     */
    function getFiles(query) {
        var fileList;

        fileList = Drive.Files.list({
                       q: query,
                       maxResults: 1000,
                       pageToken: properties.pageToken
                   });

        return fileList;
    }

    /**
     * copy permissions from source to destination file/folder
     *
     * @param {string} srcId identification string for the source folder
     * @param {string} destId identification string for the destination folder
     * @param {string} filetype options: "file" or "folder", so know how to retrieve editors and viewers
         Maybe unnecessary since folders are treated as files in Advanced Drive API?
     */
    function copyPermissions(srcId, destId, filetype) {
        var editors = 0;// get editors
        var viewers = 0;// get editors
        var owners = 0; // add owners to editors

        // these need adjustment based on my variable declarations
        if (editors.length > 0) {
            for (var i = 0; i < editorsemails.length; i++) {
                Drive.Permissions.insert(
                    {
                        'role': 'writer',
                        'type': 'user',
                        'value': editorsemails[i]
                    },
                    destId,
                    {
                        'sendNotificationEmails': 'false'
                    });
            }
        }

        if (viewers.length > 0) {
            for (var i = 0; i < viewersemails.length; i++) {
                Drive.Permissions.insert(
                    {
                        'role': 'reader',
                        'type': 'user',
                        'value': viewersemails[i]
                    },
                    destId,
                    {
                        'sendNotificationEmails': 'false'
                    });
            }
        }
    }

    /**
     * Delete existing triggers, save properties, and create new trigger
     */
    function saveState() {
        if ( properties.triggerId !== "undefined" ) {
            // delete prior trigger
            deleteTrigger(properties.triggerId);
        }

        // save, create trigger
        properties.currChildren = files.items ? files : properties.currChildren;
        saveProperties(properties, createTrigger);
        return;
    }

}<commit_msg>cleanup<commit_after>//todo: delete trigger after it runs - there is a max for the script

/**
 * Copy folders and files from source to destination.
 * Get parameters from userProperties,
 * Loop until time runs out,
 * then call timeout methods, saveProperties and createTrigger.
 */
function copy() {
    // CONSTANTS
    var MAX_RUNNING_TIME = 5.7 * 60 * 1000;   // 5.7 minutes in milliseconds
    var START_TIME = (new Date()).getTime();

    var timeIsUp,       // {boolean} true if max execution time is reached while executing script
        permissions,    // {boolean} true if sharing permissions should be copied from source to destination
        currTime,       // {number} integer representing current time in milliseconds
        copyObject,     // {object} all the files and folders to copy
        srcId,          // {string} identification for top level source folder
        destId,         // {string} identification for top level destination folder
        ss,             // {object} instance of Sheet class
        properties,     // {object} properties of current run
        query,          // {string} query to generate Files list
        files,          // {object} list of files within Drive folder
        item,           // {object} metadata of child item from current iteration
        currFolder,     // {object} metadata of folder whose children are currently being processed
        newfile,        // {Object} JSON metadata for newly created folder or file
        timeIsUp = false; // {boolean}

    properties = loadProperties();

    // get current children, or if none exist, query next folder from properties.remaining
    if ( properties.currChildren.items && properties.currChildren.items.length > 0) {

        processFiles(properties.currChildren.items)

    }

    // when currChildren is complete, move on to other queries from properties.remaining

    while ( properties.remaining.length > 0 && !timeIsUp) {

        currFolder = properties.remaining.shift();
        query = '"' + currFolder + '" in parents and trashed = false';
        // Note: pageToken will only be generated IF there are results on the next "page".  So I always want to test for it, but if it isn't present, then that's ok.  However, it can sort of be like my "continuationToken", maybe

        do {

            // get files
            files = getFiles(query);

            // loop through and process
            if (files.items && files.items.length > 0) {

                processFiles(files.items);

            } else {

                Logger.log('No children found.');
                // todo: get next folder from properties.remaining
            }

            // get next page token to continue iteration
            properties.pageToken = files.nextPageToken;

        } while (properties.pageToken && !timeIsUp);

    }

    if ( timeIsUp ) {
        saveState();
    } else {
        deleteTrigger(properties.triggerId);
    }

    return;

    /**
     * Loops through array of files.items
     * and sends to the appropriate copy function (insertFolder or copyFile)
     *
     * @param {array} items the list of files over which to iterate
     */
    function processFiles(items) {
        while ( files.items.length > 0 && !timeIsUp ) {
            item = files.items.pop();
            currTime = (new Date()).getTime();
            timeIsUp = (currTime - START_TIME >= MAX_RUNNING_TIME);

            if ( item.mimeType == "application/vnd.google-apps.folder") {
                newfile = insertFolder(item);
            } else {
                newfile = copyFile(folders.items[i]);
            }

            if (newfile.id) {
                // sheet.getRange(row, column, numRows, numColumns)
                ss.getRange(ss.getLastRow(), 1, 1, 3).setValues([
                    "Copied",
                    newfile.id,
                    newfile.title,
                    newfile.defaultOpenWithLink,
                    Utilities.formatDate(new Date(), "GMT-5", "MM-dd-yy hh:mm a")
                ]);

            } else {
                // newfile is error message
                ss.getRange(ss.getLastRow(), 1, 1, 2).setValue([
                    "Error",
                    newfile
                ]);
            }

        }
        return;
    }

    /**
     * Try to insert folder with information from src file.
     * Success:
     *   1. Add key/value to properties.map with src/dest folder ID pair
     *   2. Add dest folder ID to properties.remaining array
     *   3. Add note to Logger spreadsheet with destination folder ID
     * Failure:
     *   1. Log failure in spreadsheet with src ID
     *
     * @pararm {Object} file File Resource with metadata from source folder
     */
    function insertFolder(file) {

        try {
            var r = Drive.Files.insert({
                "description": file.description,
                "title": file.title,
                "parents": [
                    {
                        "kind": "drive#fileLink",
                        "id": properties.map[file.parents[0].id]
                    }
                ],
                "mimeType": "application/vnd.google-apps.folder"
            });

            properties.remaining.push(r.id);

            return;
        }

        catch(err) {
            Logger.log(err.message);
            return err.message;
        }
    }

    /**
     * Try to copy file to destination parent.
     * Success:
     *   1. Log success in spreadsheet with file ID
     * Failure:
     *   1. Log error in spreadsheet with source ID
     *
     * @param {Object} file File Resource with metadata from source file
     */
    function copyFile(file) {
        try {
            var r = Drive.Files.copy(
                {
                "title": file.title,
                "parents": [
                    {
                        "kind": "drive#fileLink",
                        "id": properties.map[file.parents[0].id]
                    }
                ]
                },
                file.id
            );
            return r;
        }

        catch(err) {
            Logger.log(err.message);
            return err;
        }
    }

    /**
     * Gets files from query and returns fileList with metadata
     *
     * @param {string} query the query to select files from the Drive
     * @return {object} fileList object where fileList.items is an array of children files
     */
    function getFiles(query) {
        var fileList;

        fileList = Drive.Files.list({
                       q: query,
                       maxResults: 1000,
                       pageToken: properties.pageToken
                   });

        return fileList;
    }

    /**
     * copy permissions from source to destination file/folder
     *
     * @param {string} srcId identification string for the source folder
     * @param {string} destId identification string for the destination folder
     * @param {string} filetype options: "file" or "folder", so know how to retrieve editors and viewers
         Maybe unnecessary since folders are treated as files in Advanced Drive API?
     */
    function copyPermissions(srcId, destId, filetype) {
        var editors = 0;// get editors
        var viewers = 0;// get editors
        var owners = 0; // add owners to editors

        // these need adjustment based on my variable declarations
        if (editors.length > 0) {
            for (var i = 0; i < editorsemails.length; i++) {
                Drive.Permissions.insert(
                    {
                        'role': 'writer',
                        'type': 'user',
                        'value': editorsemails[i]
                    },
                    destId,
                    {
                        'sendNotificationEmails': 'false'
                    });
            }
        }

        if (viewers.length > 0) {
            for (var i = 0; i < viewersemails.length; i++) {
                Drive.Permissions.insert(
                    {
                        'role': 'reader',
                        'type': 'user',
                        'value': viewersemails[i]
                    },
                    destId,
                    {
                        'sendNotificationEmails': 'false'
                    });
            }
        }
    }

    /**
     * Delete existing triggers, save properties, and create new trigger
     */
    function saveState() {
        if ( properties.triggerId !== undefined ) {
            // delete prior trigger
            deleteTrigger(properties.triggerId);
        }

        // save, create trigger
        properties.currChildren = files.items ? files : properties.currChildren;
        saveProperties(properties, createTrigger);
        return;
    }

}<|endoftext|>


<commit_before>var IgnoreType = function(){};

var helpers;
module.exports = helpers = {
	extend: function(d, s){
		for(var prop in s) {
			d[prop] = s[prop];
		}
		return d;
	},
	isArrayLike: function(arr){
		return arr && (typeof arr === "object") && (typeof arr.length === "number") && (arr.length >= 0) && (arr.length === 0 || ( (arr.length-1) in arr) );
	},
	each: function(obj, cb){
		if(helpers.isArrayLike(obj)) {
			for(var i = 0 ; i < obj.length; i++) {
				if( cb( obj[i], i ) === false ) {
					break;
				}
			}
		} else {
			for(var prop in obj) {
				if( obj.hasOwnProperty(prop) ) {
					if( cb( obj[prop], prop ) === false ) {
						break;
					}
				}
			}
		}
		return obj;
	},
	// loops through all unique props in a and then in b
	eachInUnique: function(a, acb, b, bcb, defaultReturn){
		var bCopy = helpers.extend({}, b),
			res;
		for (var prop in a) {
			res = acb(a[prop], b[prop], a, b, prop );
			if(res !== undefined) {
				return res;
			}
			delete bCopy[prop];
		}
		for (prop in bCopy) {
			res = bcb(undefined, b[prop], a, b, prop );
			if(res !== undefined) {
				return res;
			}
		}
		return defaultReturn;
	},
	makeArray: function(arr){
		var array = [];
		helpers.each(arr, function(item){
			array.push(item);
		});
		return array;
	},
	doubleLoop: function(arr, callbacks){
		if(typeof callbacks === "function") {
			callbacks = {iterate: callbacks};
		}
		var i = 0;
		while(i < arr.length) {
			if(callbacks.start) {
				callbacks.start(arr[i]);
			}

			var j = i+1;
			while( j < arr.length ) {
				if(callbacks.iterate(arr[j], j, arr[i], i) === false) {
					arr.splice(j, 1);
				} else {
					j++;
				}
			}
			if(callbacks.end) {
				callbacks.end(arr[i]);
			}
			i++;
		}
	},
	identityMap: function(arr){
		var map = {};
		helpers.each(arr, function(value){
			map[value] = 1;
		});
		return map;
	},
	arrayUnionIntersectionDifference: function(arr1, arr2){
		var map = {};

		var intersection = [];
		var union = [];
		var difference = arr1.slice(0);

		helpers.each(arr1, function(value){
			map[value] = true;
			union.push(value);
		});

		helpers.each(arr2, function(value){
			if(map[value]) {
				intersection.push(value);
				var index = helpers.indexOf.call(difference, value);
				if(index !== -1) {
					difference.splice(index, 1);
				}

			} else {
				union.push(value);
			}
		});

		return {
			intersection: intersection,
			union: union,
			difference: difference
		};
	},
	arraySame: function(arr1, arr2){
		if(arr1.length !== arr2.length) {
			return false;
		}
		var map = helpers.identityMap(arr1);
		for(var i = 0 ; i < arr2.length; i++) {
			var val = map[arr2[i]];
			if(!val) {
				return false;
			} else if(val > 1){
				return false;
			} else {
				map[arr2[i]]++;
			}
		}
		return true;
	},
	indexOf: Array.prototype.indexOf || function(item) {
		for (var i = 0, thisLen = this.length; i < thisLen; i++) {
			if (this[i] === item) {
				return i;
			}
		}
		return -1;
	},
	map: Array.prototype.map || function(cb){
		var out = [];
		for(var i = 0, len = this.length; i < len; i++) {
			out.push(cb(this[i], i, this));
		}
		return out;
	},
	filter: Array.prototype.filter || function(cb){
		var out = [];
		for(var i = 0, len = this.length; i < len; i++) {
			if(cb(this[i], i, this)) {
				out.push(this[i]);
			}
		}
		return out;
	},
	isEmptyObject: function(obj) {
		var prop;

		for(prop in obj) {
			if(obj.hasOwnProperty(prop)) {
				break;
			}
		}

		return prop === undefined;
	},
	// This is a dummy object that can signal to be ignored
	ignoreType: new IgnoreType(),
	firstProp: function(set){
		for(var prop in set) {
			return prop;
		}
	},
	last: function(arr){
		return arr[arr.length -1];
	},
	index: function(compare, items, props){
		// check the start and the end
		if( compare(props, items[0]) === -1 ) {
			return 0;
		}
		else if(compare(props, helpers.last(items) ) === 1 ) {
			return items.length;
		}
		var low = 0,
			high = items.length;

		// From lodash lodash 4.6.1 <https://lodash.com/>
		// Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>
		while (low < high) {
			var mid = (low + high) >>> 1,
				item = items[mid],
				computed = compare(props, item);
			if ( computed === -1 ) {
				high = mid;
			} else {
				low = mid + 1;
			}
		}
		return high;
		// bisect by calling sortFunc
	},
	defaultSort: function(sortPropValue, item1, item2) {
		var parts = sortPropValue.split(' ');
		var sortProp = parts[0];
		var item1Value = item1[sortProp];
		var item2Value = item2[sortProp];
		var temp;
		var desc = parts[1] || '';
		desc = desc.toLowerCase()	=== 'desc';

		if(desc) {
			temp = item1Value;
			item1Value = item2Value;
			item2Value = temp;
		}

		if(item1Value < item2Value) {
			return -1;
		}

		if(item1Value > item2Value) {
			return 1;
		}

		return 0;
	}
};
<commit_msg>protect against empty lists<commit_after>var IgnoreType = function(){};

var helpers;
module.exports = helpers = {
	extend: function(d, s){
		for(var prop in s) {
			d[prop] = s[prop];
		}
		return d;
	},
	isArrayLike: function(arr){
		return arr && (typeof arr === "object") && (typeof arr.length === "number") && (arr.length >= 0) && (arr.length === 0 || ( (arr.length-1) in arr) );
	},
	each: function(obj, cb){
		if(helpers.isArrayLike(obj)) {
			for(var i = 0 ; i < obj.length; i++) {
				if( cb( obj[i], i ) === false ) {
					break;
				}
			}
		} else {
			for(var prop in obj) {
				if( obj.hasOwnProperty(prop) ) {
					if( cb( obj[prop], prop ) === false ) {
						break;
					}
				}
			}
		}
		return obj;
	},
	// loops through all unique props in a and then in b
	eachInUnique: function(a, acb, b, bcb, defaultReturn){
		var bCopy = helpers.extend({}, b),
			res;
		for (var prop in a) {
			res = acb(a[prop], b[prop], a, b, prop );
			if(res !== undefined) {
				return res;
			}
			delete bCopy[prop];
		}
		for (prop in bCopy) {
			res = bcb(undefined, b[prop], a, b, prop );
			if(res !== undefined) {
				return res;
			}
		}
		return defaultReturn;
	},
	makeArray: function(arr){
		var array = [];
		helpers.each(arr, function(item){
			array.push(item);
		});
		return array;
	},
	doubleLoop: function(arr, callbacks){
		if(typeof callbacks === "function") {
			callbacks = {iterate: callbacks};
		}
		var i = 0;
		while(i < arr.length) {
			if(callbacks.start) {
				callbacks.start(arr[i]);
			}

			var j = i+1;
			while( j < arr.length ) {
				if(callbacks.iterate(arr[j], j, arr[i], i) === false) {
					arr.splice(j, 1);
				} else {
					j++;
				}
			}
			if(callbacks.end) {
				callbacks.end(arr[i]);
			}
			i++;
		}
	},
	identityMap: function(arr){
		var map = {};
		helpers.each(arr, function(value){
			map[value] = 1;
		});
		return map;
	},
	arrayUnionIntersectionDifference: function(arr1, arr2){
		var map = {};

		var intersection = [];
		var union = [];
		var difference = arr1.slice(0);

		helpers.each(arr1, function(value){
			map[value] = true;
			union.push(value);
		});

		helpers.each(arr2, function(value){
			if(map[value]) {
				intersection.push(value);
				var index = helpers.indexOf.call(difference, value);
				if(index !== -1) {
					difference.splice(index, 1);
				}

			} else {
				union.push(value);
			}
		});

		return {
			intersection: intersection,
			union: union,
			difference: difference
		};
	},
	arraySame: function(arr1, arr2){
		if(arr1.length !== arr2.length) {
			return false;
		}
		var map = helpers.identityMap(arr1);
		for(var i = 0 ; i < arr2.length; i++) {
			var val = map[arr2[i]];
			if(!val) {
				return false;
			} else if(val > 1){
				return false;
			} else {
				map[arr2[i]]++;
			}
		}
		return true;
	},
	indexOf: Array.prototype.indexOf || function(item) {
		for (var i = 0, thisLen = this.length; i < thisLen; i++) {
			if (this[i] === item) {
				return i;
			}
		}
		return -1;
	},
	map: Array.prototype.map || function(cb){
		var out = [];
		for(var i = 0, len = this.length; i < len; i++) {
			out.push(cb(this[i], i, this));
		}
		return out;
	},
	filter: Array.prototype.filter || function(cb){
		var out = [];
		for(var i = 0, len = this.length; i < len; i++) {
			if(cb(this[i], i, this)) {
				out.push(this[i]);
			}
		}
		return out;
	},
	isEmptyObject: function(obj) {
		var prop;

		for(prop in obj) {
			if(obj.hasOwnProperty(prop)) {
				break;
			}
		}

		return prop === undefined;
	},
	// This is a dummy object that can signal to be ignored
	ignoreType: new IgnoreType(),
	firstProp: function(set){
		for(var prop in set) {
			return prop;
		}
	},
	last: function(arr){
		return arr[arr.length -1];
	},
	index: function(compare, items, props){
		if(!items || !items.length) {
			return undefined;
		}
		// check the start and the end
		if( compare(props, items[0]) === -1 ) {
			return 0;
		}
		else if(compare(props, helpers.last(items) ) === 1 ) {
			return items.length;
		}
		var low = 0,
			high = items.length;

		// From lodash lodash 4.6.1 <https://lodash.com/>
		// Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>
		while (low < high) {
			var mid = (low + high) >>> 1,
				item = items[mid],
				computed = compare(props, item);
			if ( computed === -1 ) {
				high = mid;
			} else {
				low = mid + 1;
			}
		}
		return high;
		// bisect by calling sortFunc
	},
	defaultSort: function(sortPropValue, item1, item2) {
		var parts = sortPropValue.split(' ');
		var sortProp = parts[0];
		var item1Value = item1[sortProp];
		var item2Value = item2[sortProp];
		var temp;
		var desc = parts[1] || '';
		desc = desc.toLowerCase()	=== 'desc';

		if(desc) {
			temp = item1Value;
			item1Value = item2Value;
			item2Value = temp;
		}

		if(item1Value < item2Value) {
			return -1;
		}

		if(item1Value > item2Value) {
			return 1;
		}

		return 0;
	}
};
<|endoftext|>