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

    var exec,
        rendy,
        Scope = scope.window ? window : global;

    if (typeof module === 'object' && module.exports) {
        exec = require('execon');
        rendy = require('rendy');
        module.exports = new UtilProto(exec);
    } else if (!Scope.Util) {
        exec = window.exec;
        rendy = window.rendy;
        Scope.Util = new UtilProto(exec);
    }

    function UtilProto(exec) {
        var Util = this;

        this.check              = new checkProto();

        function checkProto() {
            /**
             * Check is all arguments with names present
             *
             * @param name
             * @param arg
             */
            function check(args, names) {
                var msg         = '',
                    name        = '',
                    template    = '{{ name }} coud not be empty!',

                    indexOf     = Array.prototype.indexOf,

                    lenNames    = names.length,
                    lenArgs     = args.length,
                    lessArgs    = lenArgs < lenNames,
                    emptyIndex  = indexOf.call(args),
                    isEmpty     = ~emptyIndex && emptyIndex <= lenNames - 1;

                if (lessArgs || isEmpty) {
                    if (lessArgs)
                        name        = names[lenNames - 1];
                    else
                        name        = names[emptyIndex];

                    msg         = rendy(template, {
                        name: name
                    });

                    throw(Error(msg));
                }

                return check;
            };

            check.check = check;

            /**
             * Check is type of arg with name is equal to type
             *
             * @param name
             * @param arg
             * @param type
             */
            check.type  = function(name, arg, type) {
                var is = Util.type(arg) === type;

                if (!is)
                    throw(Error(name + ' should be ' + type));

                return check;
            };

            return check;
        }

        /**
         * Copy properties from from to to
         *
         * @param from
         * @param to
         */
        this.copyObj                = function(to, from) {
            if (!from) {
                from    = to;
                to      = {};
            }

            if (to)
                Object.keys(from).forEach(function(name) {
                    to[name]    = from[name];
                });

            return to;
        };

        /**
         * copy objFrom properties to target
         *
         * @target
         * @objFrom
         */
        this.extend                 = function(target, objFrom) {
            var obj,
                keys,
                proto,
                isFunc  = Util.type.function(objFrom),
                isArray = Util.type.array(objFrom),
                isObj   = Util.type.object(target),
                ret     = isObj ? target : {};

            if (isArray)
                objFrom.forEach(function(item) {
                   ret = Util.extend(target, item);
                });

            else if (objFrom) {
                obj     = isFunc ? new objFrom() : objFrom;
                keys    = Object.keys(obj);

                if (!keys.length) {
                    proto = Object.getPrototypeOf(objFrom);
                    keys  = Object.keys(proto);
                }

                keys.forEach(function(name) {
                    ret[name] = obj[name];
                });
            }

            return ret;
        };

        /**
         * extend proto
         *
         * @obj
         */
        this.extendProto           = function(obj) {
            var ret, F      = function() {};
            F.prototype     = Util.extend({}, obj);
            ret             = new F();

            return ret;
        };

        this.json               = new JsonProto();

        function JsonProto() {
            /**
             * @param str
             */
            this.parse      = function(str) {
                var obj;

                Util.exec.try(function() {
                    obj = JSON.parse(str);
                });

                return obj;
            },

            /**
             * @param obj
             */
            this.stringify  = function(obj) {
                var str;

                Util.exec.try(function() {
                    str = JSON.stringify(obj, null, 4);
                });

                return str;
            };
        }

        this.escapeRegExp = function(str) {
            var isStr   = Util.type.string(str);

            if (isStr)
                str = str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');

            return str;
        };

        /**
         * get regexp from wild card
         */
        this.getRegExp              = function(wildcard) {
            var regExp;

            if (!wildcard)
                wildcard = '*';

            wildcard    = '^' + wildcard /* search from start of line */
                .replace('.', '\\.')
                .replace('*', '.*')
                .replace('?', '.?');

            wildcard    += '$'; /* search to end of line */

            regExp      = new RegExp(wildcard);

            return regExp;
        };

        this.type                   = new TypeProto();

        function TypeProto() {
            /**
             * get type of variable
             *
             * @param variable
             */
            function type(variable) {
                var regExp      = /\s([a-zA-Z]+)/,
                    str         = {}.toString.call(variable),
                    typeBig     = str.match(regExp)[1],
                    result      = typeBig.toLowerCase();

                return result;
            }

            /**
             * functions check is variable is type of name
             *
             * @param variable
             */
            function typeOf(name, variable) {
                return type(variable) === name;
            }

            function typeOfSimple(name, variable) {
                return typeof variable === name;
            }

            ['null', 'arrayBuffer', 'file', 'array', 'object']
                .forEach(function(name) {
                    type[name] = typeOf.bind(null, name);
                });

            ['string', 'undefined', 'boolean', 'number', 'function']
                .forEach(function(name) {
                    type[name] = typeOfSimple.bind(null, name);
                });

            return type;
        }

        this.exec                       = exec;

        /**
         * function gets file extension
         *
         * @param pFileName
         * @return Ext
         */
        this.getExt                     = function(name) {
            var ret     = '',
                dot,
                isStr   = Util.type.string(name);

            if (isStr) {
                dot = name.lastIndexOf('.');

                if (~dot)
                    ret = name.substr(dot);
            }

            return ret;
        };

       /**
         * get values from Object Array name properties
         * or
         * @pObj
         */
        this.getNamesFromObjArray       = function(arr) {
            var ret     = [];

            if (!Array.isArray(arr))
                throw(Error('arr should be array!'));

            ret = arr.map(function(item) {
                return item.name;
            });

            return ret;
        };

        /**
         * find object by name in arrray
         *
         * @param array
         * @param name
         */
        this.findObjByNameInArr         = function(array, name) {
            var ret;

             if (!Array.isArray(array))
                throw(Error('array should be array!'));

            if (typeof name !== 'string')
                throw(Error('name should be string!'));

            array.some(function(item) {
                var is = item.name === name,
                    isArray = Util.type.array(item);

                if (is)
                    ret = item;
                else if (isArray)
                    item.some(function(item) {
                        is = item.name === name;

                        if (is)
                            ret = item.data;

                        return is;
                    });

                return is;
            });

            return ret;
        };

        /**
         * start timer
         * @param name
         */
        this.time                       = function(name) {
            var console     = Scope.console;

            Util.exec.ifExist(console, 'time', [name]);

            return this;
        };

        /**
         * stop timer
         * @param name
         */
        this.timeEnd                   = function(name) {
            var console    = Scope.console;

            Util.exec.ifExist(console, 'timeEnd', [name]);

            return this;
        };
    }

})(this);
<commit_msg>chore(util) rm ";"<commit_after>(function(scope) {
    'use strict';

    var exec,
        rendy,
        Scope = scope.window ? window : global;

    if (typeof module === 'object' && module.exports) {
        exec = require('execon');
        rendy = require('rendy');
        module.exports = new UtilProto(exec);
    } else if (!Scope.Util) {
        exec = window.exec;
        rendy = window.rendy;
        Scope.Util = new UtilProto(exec);
    }

    function UtilProto(exec) {
        var Util = this;

        this.check              = new checkProto();

        function checkProto() {
            /**
             * Check is all arguments with names present
             *
             * @param name
             * @param arg
             */
            function check(args, names) {
                var msg         = '',
                    name        = '',
                    template    = '{{ name }} coud not be empty!',

                    indexOf     = Array.prototype.indexOf,

                    lenNames    = names.length,
                    lenArgs     = args.length,
                    lessArgs    = lenArgs < lenNames,
                    emptyIndex  = indexOf.call(args),
                    isEmpty     = ~emptyIndex && emptyIndex <= lenNames - 1;

                if (lessArgs || isEmpty) {
                    if (lessArgs)
                        name        = names[lenNames - 1];
                    else
                        name        = names[emptyIndex];

                    msg         = rendy(template, {
                        name: name
                    });

                    throw(Error(msg));
                }

                return check;
            }

            check.check = check;

            /**
             * Check is type of arg with name is equal to type
             *
             * @param name
             * @param arg
             * @param type
             */
            check.type  = function(name, arg, type) {
                var is = Util.type(arg) === type;

                if (!is)
                    throw(Error(name + ' should be ' + type));

                return check;
            };

            return check;
        }

        /**
         * Copy properties from from to to
         *
         * @param from
         * @param to
         */
        this.copyObj                = function(to, from) {
            if (!from) {
                from    = to;
                to      = {};
            }

            if (to)
                Object.keys(from).forEach(function(name) {
                    to[name]    = from[name];
                });

            return to;
        };

        /**
         * copy objFrom properties to target
         *
         * @target
         * @objFrom
         */
        this.extend                 = function(target, objFrom) {
            var obj,
                keys,
                proto,
                isFunc  = Util.type.function(objFrom),
                isArray = Util.type.array(objFrom),
                isObj   = Util.type.object(target),
                ret     = isObj ? target : {};

            if (isArray)
                objFrom.forEach(function(item) {
                   ret = Util.extend(target, item);
                });

            else if (objFrom) {
                obj     = isFunc ? new objFrom() : objFrom;
                keys    = Object.keys(obj);

                if (!keys.length) {
                    proto = Object.getPrototypeOf(objFrom);
                    keys  = Object.keys(proto);
                }

                keys.forEach(function(name) {
                    ret[name] = obj[name];
                });
            }

            return ret;
        };

        /**
         * extend proto
         *
         * @obj
         */
        this.extendProto           = function(obj) {
            var ret, F      = function() {};
            F.prototype     = Util.extend({}, obj);
            ret             = new F();

            return ret;
        };

        this.json               = new JsonProto();

        function JsonProto() {
            /**
             * @param str
             */
            this.parse      = function(str) {
                var obj;

                Util.exec.try(function() {
                    obj = JSON.parse(str);
                });

                return obj;
            },

            /**
             * @param obj
             */
            this.stringify  = function(obj) {
                var str;

                Util.exec.try(function() {
                    str = JSON.stringify(obj, null, 4);
                });

                return str;
            };
        }

        this.escapeRegExp = function(str) {
            var isStr   = Util.type.string(str);

            if (isStr)
                str = str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');

            return str;
        };

        /**
         * get regexp from wild card
         */
        this.getRegExp              = function(wildcard) {
            var regExp;

            if (!wildcard)
                wildcard = '*';

            wildcard    = '^' + wildcard /* search from start of line */
                .replace('.', '\\.')
                .replace('*', '.*')
                .replace('?', '.?');

            wildcard    += '$'; /* search to end of line */

            regExp      = new RegExp(wildcard);

            return regExp;
        };

        this.type                   = new TypeProto();

        function TypeProto() {
            /**
             * get type of variable
             *
             * @param variable
             */
            function type(variable) {
                var regExp      = /\s([a-zA-Z]+)/,
                    str         = {}.toString.call(variable),
                    typeBig     = str.match(regExp)[1],
                    result      = typeBig.toLowerCase();

                return result;
            }

            /**
             * functions check is variable is type of name
             *
             * @param variable
             */
            function typeOf(name, variable) {
                return type(variable) === name;
            }

            function typeOfSimple(name, variable) {
                return typeof variable === name;
            }

            ['null', 'arrayBuffer', 'file', 'array', 'object']
                .forEach(function(name) {
                    type[name] = typeOf.bind(null, name);
                });

            ['string', 'undefined', 'boolean', 'number', 'function']
                .forEach(function(name) {
                    type[name] = typeOfSimple.bind(null, name);
                });

            return type;
        }

        this.exec                       = exec;

        /**
         * function gets file extension
         *
         * @param pFileName
         * @return Ext
         */
        this.getExt                     = function(name) {
            var ret     = '',
                dot,
                isStr   = Util.type.string(name);

            if (isStr) {
                dot = name.lastIndexOf('.');

                if (~dot)
                    ret = name.substr(dot);
            }

            return ret;
        };

       /**
         * get values from Object Array name properties
         * or
         * @pObj
         */
        this.getNamesFromObjArray       = function(arr) {
            var ret     = [];

            if (!Array.isArray(arr))
                throw(Error('arr should be array!'));

            ret = arr.map(function(item) {
                return item.name;
            });

            return ret;
        };

        /**
         * find object by name in arrray
         *
         * @param array
         * @param name
         */
        this.findObjByNameInArr         = function(array, name) {
            var ret;

             if (!Array.isArray(array))
                throw(Error('array should be array!'));

            if (typeof name !== 'string')
                throw(Error('name should be string!'));

            array.some(function(item) {
                var is = item.name === name,
                    isArray = Util.type.array(item);

                if (is)
                    ret = item;
                else if (isArray)
                    item.some(function(item) {
                        is = item.name === name;

                        if (is)
                            ret = item.data;

                        return is;
                    });

                return is;
            });

            return ret;
        };

        /**
         * start timer
         * @param name
         */
        this.time                       = function(name) {
            var console     = Scope.console;

            Util.exec.ifExist(console, 'time', [name]);

            return this;
        };

        /**
         * stop timer
         * @param name
         */
        this.timeEnd                   = function(name) {
            var console    = Scope.console;

            Util.exec.ifExist(console, 'timeEnd', [name]);

            return this;
        };
    }

})(this);
<|endoftext|>


<commit_before>var Q = require('q'),
    QFS = require('q-fs'),
    PATH = require('./path'),
    FS = require('fs'),
    require = requireWrapper(require);

exports.chdirOptParse = function() {
    return this.opt()
        .name('dir').short('C').long('chdir')
        .title('working directory, cwd by default')
        .def(process.cwd())
        .val(function(d) {
            d = PATH.join(d, PATH.dirSep);
            process.chdir(d);
            return d;
        })
        .end();
};

exports.techsOptParse = function() {
    return this
        .opt()
            .name('addTech').short('t').long('add-tech')
            .title('add tech')
            .arr()
            .end()
        .opt()
            .name('forceTech').short('T').long('force-tech')
            .title('use only specified tech')
            .arr()
            .end()
        .opt()
            .name('noTech').short('n').long('no-tech')
            .title('exclude tech')
            .arr()
            .end()
};

exports.levelOptParse = function() {
    var def = exports.findLevel(process.cwd()),
        rel = PATH.relative(PATH.join(process.cwd(), PATH.dirSep), def);
    return this.opt()
        .name('level').short('l').long('level')
        .def(def)
        .title(['level directory path, default: ',
            !rel? '.' : rel, PATH.dirSep].join(''))
        .val(function(l) {
            return typeof l == 'string'? require('./level', true).createLevel(l) : l;
        })
        .end();
};

exports.mergeTechs = function(level, opts) {
    // NOTE: если при создании блока/элемента/модификатора
    // указали --force-tech <name> или --no-tech, и в level.js
    // определена технология с таким именем/файлом на диске,
    // нужно использовать именно её
    var techs = opts.forceTech? {} : level.techs,
        optsTechs = [];

    opts.forceTech && optsTechs.push.apply(optsTechs, opts.forceTech);
    opts.addTech && optsTechs.push.apply(optsTechs, opts.addTech);

    optsTechs.forEach(function(t) {
        var tech = level.getTech(t),
            name = tech.getTechName();
        techs[name] || (techs[name] = tech);
    });

    opts.noTech && opts.noTech.forEach(function(t) {
        delete techs[level.getTech(t).getTechName()];
    });

    return techs;
};

exports.write = function(path, content) {
    FS.writeFileSync(path, Array.isArray(content) ? content.join('') : content);
};

exports.writeFile = function(path, content) {
    return Q.when(content, function(content) {
        return QFS.write(path, Array.isArray(content) ? content.join('') : content, { charset: 'utf8' });
    });
};

exports.readFile = function(path) {
    return QFS.read(path, { charset: 'utf8' });
};

exports.readBinary = function(path) {
    return QFS.read(path, { charset: 'binary' });
};

exports.mkdir = function(path) {
    try { FS.mkdirSync(path, 0777) } catch(ignore) {}
};

exports.mkdirs = function(path) {
    if (PATH.existsSync(path)) return;
    exports.mkdirs(PATH.dirname(path));
    exports.mkdir(path);
};

exports.isExists = function(path) {
    var d = Q.defer();
    PATH.exists(path, function(res) {
        d.resolve(res);
    });
    return d.promise;
};

exports.isFile = function(path) {
    try {
        return FS.statSync(path).isFile();
    } catch(ignore) {}
    return false;
};

exports.isFileP = function(path) {
    return Q.when(QW.execute(FS.stat, path),
        function(stat) {
            return stat.isFile();
        },
        function(err) {
            return false;
        });
};

exports.isDirectory = function(path) {
    try {
        return FS.statSync(path).isDirectory();
    } catch(ignore) {}
    return false;
};

exports.isLevel = function(path) {
    return exports.isDirectory(path) &&
        exports.isFile(PATH.join(path, '.bem', 'level.js'));
};

exports.findLevel = function(path, startPath) {
    startPath = startPath || path;
    if (exports.isLevel(path)) return path;
    if (PATH.isRoot(path)) return startPath;
    return exports.findLevel(PATH.dirname(path), startPath);
};

exports.fsWalkTree = function(root, fileCb, filterCb) {
    var files = FS.readdirSync(root);
    files.sort();
    while (files.length > 0) {
        var path = PATH.join(root, files.shift());
        if(filterCb && !filterCb(path)) continue;
        fileCb(path);
        if(exports.isDirectory(path)) exports.fsWalkTree(path, fileCb, filterCb);
    }
};

exports.getDirs = function(path) {
    try {
        return exports.isDirectory(path)?
            FS.readdirSync(path)
                .filter(function(d) {
                    return !(/^\.svn$/.test(d)) && exports.isDirectory(PATH.join(path, d));
                })
                .sort() :
            [];
    } catch (e) {
        return [];
    }
};

exports.getDirsAsync = function(path) {
    return QFS.list(path).then(function(items) {
        return Q.all(items.map(function(i) {
            return QFS.isDirectory(PATH.join(path, i))
                .then(function(isDir){
                    return {
                        name: i,
                        dir: isDir
                    };
                }
            );
        }))
        .then(function(items) {
                return items
                    .filter(function(item) {
                        return item.dir;
                    })
                    .map(function(item) {
                        return item.name;
                    });
            }
        );
    });
};

exports.getFiles = function(path) {
    try {
        return exports.isDirectory(path)?
            FS.readdirSync(path)
                .filter(function(f) {
                    return exports.isFile(PATH.join(path, f));
                })
                .sort() :
            [];
    } catch (e) {
        return [];
    }
};

exports.isEmptyObject = function(obj) {
    for(var i in obj) return false;
    return true;
};

exports.isRequireError = function(e) {
    return /^Cannot find module/.test(e.message);
};

exports.isPath = function(str) {
    return PATH.normalize(str).indexOf(PATH.dirSep) !== -1;
};

exports.isRequireable = function(path) {
    try {
        require.resolve(path);
        return true;
    } catch (e) {
        if(! exports.isRequireError(e)) throw e;
        return false;
    }
};

exports.arrayUnique = function(arr) {
    return arr.reduce(function(prev, cur) {
        if(prev.indexOf(cur) + 1) return prev;
        return prev.concat([cur]);
    }, []);
};

exports.arrayReverse = function(arr) {
    return arr.reduceRight(function(prev, cur) {
        prev.push(cur);
        return prev;
    }, []);
};

exports.getBemTechPath = function(name) {
    var bemTechs = PATH.unixToOs('bem/lib/techs'),
        path = PATH.join(bemTechs, name);
    if(exports.isRequireable(path)) {
        return path;
    }
    return PATH.unixToOs('bem/lib/tech');
};

exports.stripModuleExt = function(path) {
    var exts = Object.keys(require.extensions).map(function(v) {
        return v.replace(/^\./, '');
    });
    return path.replace(new RegExp('\\.(' + exts.join('|') + ')$'), '');
};

exports.getNodePaths = function() {
    return (process.env.NODE_PATH || '').split(PATH.pathSep);
};

exports.mergeDecls = function mergeDecls(d1, d2) {
    var keys = {};
    d1?
        d1.forEach(function(o) { keys[o.name || o] = o }) :
        d1 = [];

    d2.forEach(function(o2) {
        var name = o2.name || o2;
        if (keys.hasOwnProperty(name)) {
            var o1 = keys[name];
            o2.elems && (o1.elems = mergeDecls(o1.elems, o2.elems));
            o2.mods && (o1.mods = mergeDecls(o1.mods, o2.mods));
            o2.vals && (o1.vals = mergeDecls(o1.vals, o2.vals));
            o2.techs && (o1.techs = mergeDecls(o1.techs, o2.techs));
        } else {
            d1.push(o2);
            keys[name] = o2;
        }
    });

    return d1;
};

exports.declForEach = function(decl, cb) {

    var forItemWithMods = function(block, elem) {
            var item = elem || block,
                type = elem? 'elem' : 'block',
                args = elem? [block.name, elem.name] : [block.name];

            // for block and element
            cb(type, args, item);

            // for each modifier
            item.mods && item.mods.forEach(function(mod) {

                // for modifier
                cb(type + '-mod', args.concat(mod.name), mod);

                // for each modifier value
                mod.vals && mod.vals.forEach(function(val, i) {
                    if (!val.name) {
                        val = { name: val };
                        mod.vals[i] = val;
                    }
                    cb(type + '-mod-val', args.concat(mod.name, val.name), val);
                });

            });
        },
        forBlockDecl = function(block) {
            // for block
            forItemWithMods(block);

            // for each block element
            block.elems && block.elems.forEach(function(elem) {
                forItemWithMods(block, elem);
            });
        },
        forBlocksDecl = function(blocks) {
            // for each block in declaration
            blocks.forEach(forBlockDecl);
        };

    decl.name && forBlockDecl(decl);
    decl.blocks && forBlocksDecl(decl.blocks);

};

/**
 * Adopted from jquery's extend method. Under the terms of MIT License.
 *
 * http://code.jquery.com/jquery-1.4.2.js
 *
 * Modified by mscdex to use Array.isArray instead of the custom isArray method
 */
var extend = exports.extend = function() {
    // copy reference to target object
    var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

    // Handle a deep copy situation
    if (typeof target === 'boolean') {
        deep = target;
        target = arguments[1] || {};
        // skip the boolean and the target
        i = 2;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== 'object' && !typeof target === 'function')
        target = {};

    var isPlainObject = function(obj) {
        // Must be an Object.
        // Because of IE, we also have to check the presence of the constructor property.
        // Make sure that DOM nodes and window objects don't pass through, as well
        if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval)
            return false;

        var has_own_constructor = hasOwnProperty.call(obj, 'constructor');
        var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf');
        // Not own constructor property must be Object
        if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
            return false;

        // Own properties are enumerated firstly, so to speed up,
        // if last one is own, then all properties are own.

        var key, last_key;
        for (key in obj)
            last_key = key;

        return typeof last_key === 'undefined' || hasOwnProperty.call(obj, last_key);
    };

    for (; i < length; i++) {
        // Only deal with non-null/undefined values
        if ((options = arguments[i]) !== null) {
            // Extend the base object
            for (name in options) {
                if (!options.hasOwnProperty(name))
                    continue;
                src = target[name];
                copy = options[name];

                // Prevent never-ending loop
                if (target === copy)
                    continue;

                // Recurse if we're merging object literal values or arrays
                if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
                    var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};

                    // Never move original objects, clone them
                    target[name] = extend(deep, clone, copy);

                // Don't bring in undefined values
                } else if (typeof copy !== 'undefined')
                    target[name] = copy;
            }
        }
    }

    // Return the modified object
    return target;
};

exports.requireWrapper = requireWrapper;

function requireWrapper(wrappedRequire) {
   var func = function(module, noCache) {
       if (noCache) delete wrappedRequire.cache[wrappedRequire.resolve(module)];
       return wrappedRequire(module);
   };

   func.resolve = wrappedRequire.resolve;
   func.cache = wrappedRequire.cache;

   return func;
};
<commit_msg>Fix `util.requireWrapper()`<commit_after>var Q = require('q'),
    QFS = require('q-fs'),
    PATH = require('./path'),
    FS = require('fs'),
    require = requireWrapper(require);

exports.chdirOptParse = function() {
    return this.opt()
        .name('dir').short('C').long('chdir')
        .title('working directory, cwd by default')
        .def(process.cwd())
        .val(function(d) {
            d = PATH.join(d, PATH.dirSep);
            process.chdir(d);
            return d;
        })
        .end();
};

exports.techsOptParse = function() {
    return this
        .opt()
            .name('addTech').short('t').long('add-tech')
            .title('add tech')
            .arr()
            .end()
        .opt()
            .name('forceTech').short('T').long('force-tech')
            .title('use only specified tech')
            .arr()
            .end()
        .opt()
            .name('noTech').short('n').long('no-tech')
            .title('exclude tech')
            .arr()
            .end()
};

exports.levelOptParse = function() {
    var def = exports.findLevel(process.cwd()),
        rel = PATH.relative(PATH.join(process.cwd(), PATH.dirSep), def);
    return this.opt()
        .name('level').short('l').long('level')
        .def(def)
        .title(['level directory path, default: ',
            !rel? '.' : rel, PATH.dirSep].join(''))
        .val(function(l) {
            return typeof l == 'string'? require('./level', true).createLevel(l) : l;
        })
        .end();
};

exports.mergeTechs = function(level, opts) {
    // NOTE: если при создании блока/элемента/модификатора
    // указали --force-tech <name> или --no-tech, и в level.js
    // определена технология с таким именем/файлом на диске,
    // нужно использовать именно её
    var techs = opts.forceTech? {} : level.techs,
        optsTechs = [];

    opts.forceTech && optsTechs.push.apply(optsTechs, opts.forceTech);
    opts.addTech && optsTechs.push.apply(optsTechs, opts.addTech);

    optsTechs.forEach(function(t) {
        var tech = level.getTech(t),
            name = tech.getTechName();
        techs[name] || (techs[name] = tech);
    });

    opts.noTech && opts.noTech.forEach(function(t) {
        delete techs[level.getTech(t).getTechName()];
    });

    return techs;
};

exports.write = function(path, content) {
    FS.writeFileSync(path, Array.isArray(content) ? content.join('') : content);
};

exports.writeFile = function(path, content) {
    return Q.when(content, function(content) {
        return QFS.write(path, Array.isArray(content) ? content.join('') : content, { charset: 'utf8' });
    });
};

exports.readFile = function(path) {
    return QFS.read(path, { charset: 'utf8' });
};

exports.readBinary = function(path) {
    return QFS.read(path, { charset: 'binary' });
};

exports.mkdir = function(path) {
    try { FS.mkdirSync(path, 0777) } catch(ignore) {}
};

exports.mkdirs = function(path) {
    if (PATH.existsSync(path)) return;
    exports.mkdirs(PATH.dirname(path));
    exports.mkdir(path);
};

exports.isExists = function(path) {
    var d = Q.defer();
    PATH.exists(path, function(res) {
        d.resolve(res);
    });
    return d.promise;
};

exports.isFile = function(path) {
    try {
        return FS.statSync(path).isFile();
    } catch(ignore) {}
    return false;
};

exports.isFileP = function(path) {
    return Q.when(QW.execute(FS.stat, path),
        function(stat) {
            return stat.isFile();
        },
        function(err) {
            return false;
        });
};

exports.isDirectory = function(path) {
    try {
        return FS.statSync(path).isDirectory();
    } catch(ignore) {}
    return false;
};

exports.isLevel = function(path) {
    return exports.isDirectory(path) &&
        exports.isFile(PATH.join(path, '.bem', 'level.js'));
};

exports.findLevel = function(path, startPath) {
    startPath = startPath || path;
    if (exports.isLevel(path)) return path;
    if (PATH.isRoot(path)) return startPath;
    return exports.findLevel(PATH.dirname(path), startPath);
};

exports.fsWalkTree = function(root, fileCb, filterCb) {
    var files = FS.readdirSync(root);
    files.sort();
    while (files.length > 0) {
        var path = PATH.join(root, files.shift());
        if(filterCb && !filterCb(path)) continue;
        fileCb(path);
        if(exports.isDirectory(path)) exports.fsWalkTree(path, fileCb, filterCb);
    }
};

exports.getDirs = function(path) {
    try {
        return exports.isDirectory(path)?
            FS.readdirSync(path)
                .filter(function(d) {
                    return !(/^\.svn$/.test(d)) && exports.isDirectory(PATH.join(path, d));
                })
                .sort() :
            [];
    } catch (e) {
        return [];
    }
};

exports.getDirsAsync = function(path) {
    return QFS.list(path).then(function(items) {
        return Q.all(items.map(function(i) {
            return QFS.isDirectory(PATH.join(path, i))
                .then(function(isDir){
                    return {
                        name: i,
                        dir: isDir
                    };
                }
            );
        }))
        .then(function(items) {
                return items
                    .filter(function(item) {
                        return item.dir;
                    })
                    .map(function(item) {
                        return item.name;
                    });
            }
        );
    });
};

exports.getFiles = function(path) {
    try {
        return exports.isDirectory(path)?
            FS.readdirSync(path)
                .filter(function(f) {
                    return exports.isFile(PATH.join(path, f));
                })
                .sort() :
            [];
    } catch (e) {
        return [];
    }
};

exports.isEmptyObject = function(obj) {
    for(var i in obj) return false;
    return true;
};

exports.isRequireError = function(e) {
    return /^Cannot find module/.test(e.message);
};

exports.isPath = function(str) {
    return PATH.normalize(str).indexOf(PATH.dirSep) !== -1;
};

exports.isRequireable = function(path) {
    try {
        require.resolve(path);
        return true;
    } catch (e) {
        if(! exports.isRequireError(e)) throw e;
        return false;
    }
};

exports.arrayUnique = function(arr) {
    return arr.reduce(function(prev, cur) {
        if(prev.indexOf(cur) + 1) return prev;
        return prev.concat([cur]);
    }, []);
};

exports.arrayReverse = function(arr) {
    return arr.reduceRight(function(prev, cur) {
        prev.push(cur);
        return prev;
    }, []);
};

exports.getBemTechPath = function(name) {
    var bemTechs = PATH.unixToOs('bem/lib/techs'),
        path = PATH.join(bemTechs, name);
    if(exports.isRequireable(path)) {
        return path;
    }
    return PATH.unixToOs('bem/lib/tech');
};

exports.stripModuleExt = function(path) {
    var exts = Object.keys(require.extensions).map(function(v) {
        return v.replace(/^\./, '');
    });
    return path.replace(new RegExp('\\.(' + exts.join('|') + ')$'), '');
};

exports.getNodePaths = function() {
    return (process.env.NODE_PATH || '').split(PATH.pathSep);
};

exports.mergeDecls = function mergeDecls(d1, d2) {
    var keys = {};
    d1?
        d1.forEach(function(o) { keys[o.name || o] = o }) :
        d1 = [];

    d2.forEach(function(o2) {
        var name = o2.name || o2;
        if (keys.hasOwnProperty(name)) {
            var o1 = keys[name];
            o2.elems && (o1.elems = mergeDecls(o1.elems, o2.elems));
            o2.mods && (o1.mods = mergeDecls(o1.mods, o2.mods));
            o2.vals && (o1.vals = mergeDecls(o1.vals, o2.vals));
            o2.techs && (o1.techs = mergeDecls(o1.techs, o2.techs));
        } else {
            d1.push(o2);
            keys[name] = o2;
        }
    });

    return d1;
};

exports.declForEach = function(decl, cb) {

    var forItemWithMods = function(block, elem) {
            var item = elem || block,
                type = elem? 'elem' : 'block',
                args = elem? [block.name, elem.name] : [block.name];

            // for block and element
            cb(type, args, item);

            // for each modifier
            item.mods && item.mods.forEach(function(mod) {

                // for modifier
                cb(type + '-mod', args.concat(mod.name), mod);

                // for each modifier value
                mod.vals && mod.vals.forEach(function(val, i) {
                    if (!val.name) {
                        val = { name: val };
                        mod.vals[i] = val;
                    }
                    cb(type + '-mod-val', args.concat(mod.name, val.name), val);
                });

            });
        },
        forBlockDecl = function(block) {
            // for block
            forItemWithMods(block);

            // for each block element
            block.elems && block.elems.forEach(function(elem) {
                forItemWithMods(block, elem);
            });
        },
        forBlocksDecl = function(blocks) {
            // for each block in declaration
            blocks.forEach(forBlockDecl);
        };

    decl.name && forBlockDecl(decl);
    decl.blocks && forBlocksDecl(decl.blocks);

};

/**
 * Adopted from jquery's extend method. Under the terms of MIT License.
 *
 * http://code.jquery.com/jquery-1.4.2.js
 *
 * Modified by mscdex to use Array.isArray instead of the custom isArray method
 */
var extend = exports.extend = function() {
    // copy reference to target object
    var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;

    // Handle a deep copy situation
    if (typeof target === 'boolean') {
        deep = target;
        target = arguments[1] || {};
        // skip the boolean and the target
        i = 2;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== 'object' && !typeof target === 'function')
        target = {};

    var isPlainObject = function(obj) {
        // Must be an Object.
        // Because of IE, we also have to check the presence of the constructor property.
        // Make sure that DOM nodes and window objects don't pass through, as well
        if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval)
            return false;

        var has_own_constructor = hasOwnProperty.call(obj, 'constructor');
        var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf');
        // Not own constructor property must be Object
        if (obj.constructor && !has_own_constructor && !has_is_property_of_method)
            return false;

        // Own properties are enumerated firstly, so to speed up,
        // if last one is own, then all properties are own.

        var key, last_key;
        for (key in obj)
            last_key = key;

        return typeof last_key === 'undefined' || hasOwnProperty.call(obj, last_key);
    };

    for (; i < length; i++) {
        // Only deal with non-null/undefined values
        if ((options = arguments[i]) !== null) {
            // Extend the base object
            for (name in options) {
                if (!options.hasOwnProperty(name))
                    continue;
                src = target[name];
                copy = options[name];

                // Prevent never-ending loop
                if (target === copy)
                    continue;

                // Recurse if we're merging object literal values or arrays
                if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) {
                    var clone = src && (isPlainObject(src) || Array.isArray(src)) ? src : Array.isArray(copy) ? [] : {};

                    // Never move original objects, clone them
                    target[name] = extend(deep, clone, copy);

                // Don't bring in undefined values
                } else if (typeof copy !== 'undefined')
                    target[name] = copy;
            }
        }
    }

    // Return the modified object
    return target;
};

exports.requireWrapper = function(wrappedRequire) {
   var func = function(module, noCache) {
       if (noCache) delete wrappedRequire.cache[wrappedRequire.resolve(module)];
       return wrappedRequire(module);
   };

   ['resolve', 'cache', 'extensions', 'registerExtension'].forEach(function(key) {
       func[key] = wrappedRequire[key];
   });

   return func;
};
<|endoftext|>


<commit_before>/**
 * attr dico
 *      partials="html::.... || swig::....  || views::myView || ..."
 *
 *      edit-on-click="true"
 *
 *
 *      data-bind="mp3::id/my/path"
 *
 *      required, minLength, maxLength, .. or any json-schema related stuffs
 *
 *
my custom message

 *
 *
 */
"use strict";
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "../deep"], function(require, deep) {

	/*
	TODO
    put a map in Views that says :
    destructive : {
        what:false,
        how:true,
        where:true
    }
    interpretable : {
        what:true,
        how:true,
        where:true
    }
    loadable:{
        what:"deepload::func,string",
        how:"load::string",
        where:"load::string"
    }
    ==> or similar (maybe one only map that contains both parameters)
*/

	deep.ui.View = deep.compose.Classes(function(layer) {
		deep.utils.up(layer, this);
	}, {
		//what:"this::!",
		_deep_view_: true,
		how: function(what, routeParams) {
			return this.toString();
		},
		//where:"dom.appendTo::#ng-view",
		done: function(success) {
			// success == { what:.., rendered:"...", placed:jQuery }
			// should seek after particular deep.ui html attributs and tag to interpret as 'partials' or 'data-bind')...
		},
		fail: function(e) {
			deep.utils.dumpError(e);
		},
		remove: function() {
			//console.log("VIEW REMOVE : ", this.placed);
			if (this.placed && deep.jquery.DOM.isInDOM(this.placed()))
				deep.context.$(this.placed()).remove();
		},
		load: function(params) {
			// console.log("LOAD VIEW")
			var self = this;
			if (!this.how || this.condition === false)
				return null;
			if (this.condition)
				if (typeof this.condition === "function" && !this.condition.apply(params))
					return null;
			var promises = [];
			if (this.what)
				promises.push(deep(this).query("./what").deepLoad(params, false));
			else
				promises.push(null);

			if (typeof this.how === 'string')
				promises.push(deep(this).query("./how").load(params, false));
			else
				promises.push(this.how);

			if (this.where)
				if (typeof this.where === 'string')
					promises.push(deep(this).query("./where").load(params, false));
				else
					promises.push(this.where);

				// console.log("launch deep load");
			return deep.all(promises)
				.done(function(res) {
					//console.log("view deep load : res  : ", res);
					var result = {
						_deep_render_node_: true,
						what: res[0],
						how: res[1],
						where: res[2],
						params: params,
						controller: self
					};
					//console.log("view deep load done : ", res);
					return result;
				})
				.logError();
		},
		refresh: function(params, loaded, routeMatch) {
			// console.log("view refresh : params : ", params, " - loaded : ", loaded);
			var self = this;
			params = params || this.params;
			if (params && deep.isBrowser)
				this.params = params;
			var render = function(loaded) {
				// console.log("view render : ", loaded)
				if (!loaded)
					return;
				var what = null,
					how = null,
					where = null,
					rendered = null,
					placed = null;
				if (loaded.what)
					what = loaded.what;
				if (loaded.how)
					how = loaded.how;
				if (loaded.where)
					where = loaded.where;
				what = (what && what._deep_query_node_) ? what.value : what;
				how = (how && how._deep_query_node_) ? how.value : how;
				where = (where && where._deep_query_node_) ? where.value : where;

				if (what && what._deep_ocm_)
					what = what();
				if (how && how._deep_ocm_)
					how = how();
				if (where && where._deep_ocm_)
					where = where();

				if (!what)
					what = params || {};

				placed = self.placed ? self.placed() : null;

				loaded.placed = placed;
				// console.log("render with : how : ", how, what)
				if (!how)
					return loaded;
				try {
					what._ctrl = self;
					console.log("XHAT : ", what);
					if (routeMatch) {
						what._route = routeMatch;
						loaded.getRoute = self.getRoute = routeMatch.get;
						loaded.go = self.go = routeMatch.go;
					}
					if (typeof how === 'function')
						rendered = loaded.rendered = how.call(self, what);
					else
						rendered = loaded.rendered = how;
					if (where)
						placed = loaded.placed = where.call(self, loaded.rendered, loaded.placed);
					else
						placed = loaded.placed = null;
				} catch (e) {
					// console.log("error while rendering :  ",e);
					if (typeof self.fail === 'function')
						return self.fail(e) || e;
					return e;
				}

				if (placed && !deep.context.concurrency)
					self.placed = function() {
						return placed;
					};

				var promise = null;
				if (placed && deep.context.$) {
					//console.log("view placed : placed = ", placed);
					if (placed._deep_html_merged_) {
						var subprom = [];
						for (var i in placed) {
							if (i === '_deep_html_merged_')
								continue;
							subprom.push(self.enhance(placed[i]))
						}
						promise = deep.all(subprom);
					} else
						promise = self.enhance(placed);
				}
				// console.log("rendered : ", loaded);
				if (promise)
					return promise
						.done(function() {
							if (typeof self.done === "function") {
								var done = self.done;
								if (done._deep_ocm_)
									done = done();
								return done.call(self, loaded) || loaded;
							}
							return loaded;
						});
				if (typeof self.done === "function") {
					var done = self.done;
					if (done._deep_ocm_)
						done = done();
					return done.call(self, loaded) || loaded;
				}
				return loaded;
			};
			if (loaded)
				return render(loaded);
			return deep.when(this.load(params))
				.done(render)
				.fail(function(e) {
					if (typeof self.fail === 'function') {
						var fail = self.fail;
						if (fail._deep_ocm_)
							fail = fail();
						return fail.call(self, e) || e;
					}
				});
		},
		enhance: function(selector) {
			//console.log("enhance : ", selector);
			var promises = [];
			var self = this;
			deep.context.$(selector)
				.find("*")
				.each(function(index, element) {
					//console.log("try enhance on : ", this);
					var attr, i, len, enhancer;
					if (!deep.isBrowser) // CHEERIO specific
					{
						for (i = 0, len = this.length; i < len; ++i) {
							var e = this[i];
							if (!e.attribs)
								continue;
							for (var j in e.attribs) {
								attr = e.attribs[j];
								//console.log("attr : ",attr.name);
								if (j.indexOf("dp-") === 0 && deep.ui.View.htmlEnhancers[j]) {
									// console.log("HTML enhancer matched : ", attr.name);
									enhancer = deep.ui.View.htmlEnhancers[j];
									promises.push(enhancer.call(self, this));
									break;
								}
							}
						}
						return true;
					}

					if (!this.attributes)
						return;
					for (i = 0, len = this.attributes.length; i < len; ++i) {
						attr = this.attributes[i];
						//console.log("attr : ",attr.name);
						if (attr.name.indexOf("dp-") === 0 && deep.ui.View.htmlEnhancers[attr.name]) {
							// console.log("HTML enhancer matched : ", attr.name);
							enhancer = deep.ui.View.htmlEnhancers[attr.name];
							promises.push(enhancer.call(self, this));
							break;
						}
					}
				});
			if (promises.length === 0)
				return null;
			return deep.all(promises);
		},
		toString: function(pretty) {
			return "default view render.";
		}
	});

	deep.View = function(layer) {
		return new deep.ui.View(layer);
	};

	if (deep.isBrowser)
		deep.Chain.add("refresh", function() {
			var args = arguments;
			var self = this;
			var func = function(s, e) {
				var alls = [];
				deep.chain.each(self, function(v) {
					if (typeof v.refresh === "function")
						alls.push(v.refresh.apply(v, args));
					else
						alls.push(v);
				});
				return deep.all(alls)
					.done(function(alls) {
						if (!self._queried)
							return alls.shift();
						return alls;
					});
			};
			func._isDone_ = true;
			deep.chain.addInChain.call(self, func);
			return this;
		});

	deep.ui.View.htmlEnhancers = {
		"dp-partial": function(node) {
			//console.log("dp-partial")
			var $ = deep.context.$;
			var controller = this;
			var args = $(node).attr("dp-args") || null;
			return deep.getAll([$(node).attr("dp-partial"), args])
				.done(function(results) {
					//console.log("dp-partial get : ", results)
					var partials = results[0];
					var args = results[1];
					if (args)
						args._ctrl = controller;
					else
						args = {
							_ctrl: controller
						};
					if (typeof partials === 'function')
						$(node).html(partials(args));
					else
						$(node).html(partials);
					$(node).removeAttr("dp-partial");
					return controller.enhance(node);
				});
		},
		"dp-click": function(node) {
			if (!deep.isBrowser)
				return;
			//console.log("DPCLICK : descriptor = ", controller, "attributes : ", node);
			var $ = deep.context.$;
			var controller = this;
			var action = $(node).attr("dp-click");
			var args = $(node).attr("dp-args");
			if (args)
				args = args.split(",");
			$(node).click(function(e) {
				e.preventDefault();
				controller[action].apply(controller, args);
				return false;
			});
			//console.log("inside a dpclick tag : Action = ", action, " Args : ", args);
		},
		"dp-repeat": function(node) {
			//console.log("DP-REPEAT : descriptor = ", controller);
			var $ = deep.context.$;
			var controller = this;
			var rowNode = $(node);
			var rowHtml = deep.ui.swig(deep.jquery(rowNode));
			controller.myDatas.forEach(function(data) {
				rowNode.after(rowHtml(data));
			});
			rowNode.remove();
		},
		"dp-from": function(node) {
			var $ = deep.context.$;
			var controller = this;
			var from = $(node).attr("dp-from");
			return deep.get(from)
				.done(function(result) {
					//console.log("dp-from ", from," : get : ", result)
					if (typeof result === 'function')
						$(node).html(result({
							self: controller
						}));
					else
						$(node).html(result);
					$(node).removeAttr("dp-from");
					return controller.enhance(node);
				});
		},
		"dp-translate": function(node) {
			var $ = deep.context.$;
			var controller = this;
			var from = $(node).attr("dp-from");
			return deep.get(from)
				.done(function(result) {
					//console.log("dp-from ", from," : get : ", result)
					$(node).html(result[deep.context.language]);
					$(node).removeAttr("dp-translate");
				});
		}
	};
	return deep.ui.View;
});<commit_msg>work on simpler load sequence (view)<commit_after>/**
 * attr dico
 *      partials="html::.... || swig::....  || views::myView || ..."
 *
 *      edit-on-click="true"
 *
 *
 *      data-bind="mp3::id/my/path"
 *
 *      required, minLength, maxLength, .. or any json-schema related stuffs
 *
 *
my custom message

 *
 *
 */
"use strict";
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "../deep"], function(require, deep) {

	/*
	TODO
    put a map in Views that says :
    destructive : {
        what:false,
        how:true,
        where:true
    }
    interpretable : {
        what:true,
        how:true,
        where:true
    }
    loadable:{
        what:"deepload::func,string",
        how:"load::string",
        where:"load::string"
    }
    ==> or similar (maybe one only map that contains both parameters)
*/

	deep.ui.View = deep.compose.Classes(function(layer) {
		deep.utils.up(layer, this);
	}, {
		//what:"this::!",
		_deep_view_: true,
		how: function(what, routeParams) {
			return this.toString();
		},
		//where:"dom.appendTo::#ng-view",
		done: function(success) {
			// success == { what:.., rendered:"...", placed:jQuery }
			// should seek after particular deep.ui html attributs and tag to interpret as 'partials' or 'data-bind')...
		},
		fail: function(e) {
			deep.utils.dumpError(e);
		},
		remove: function() {
			//console.log("VIEW REMOVE : ", this.placed);
			if (this.placed && deep.jquery.DOM.isInDOM(this.placed()))
				deep.context.$(this.placed()).remove();
		},
		load: function(params) {
			// console.log("LOAD VIEW")
			var self = this;
			if (!this.how || this.condition === false)
				return null;
			if (this.condition)
				if (typeof this.condition === "function" && !this.condition.apply(params))
					return null;
			var promises = [];
			if (this.what)
				promises.push(deep(this).query("./what").deepLoad(params, false));
			else
				promises.push(null);

			if (typeof this.how === 'string')
				promises.push(deep(this).query("./how").deepLoad(params, false, true));
			else
				promises.push(this.how);

			if (this.where)
				if (typeof this.where === 'string')
					promises.push(deep(this).query("./where").deepLoad(params, false, true));
				else
					promises.push(this.where);

				// console.log("launch deep load");
			return deep.all(promises)
				.done(function(res) {
					//console.log("view deep load : res  : ", res);
					var result = {
						_deep_render_node_: true,
						what: res[0],
						how: res[1],
						where: res[2],
						params: params,
						controller: self
					};
					//console.log("view deep load done : ", res);
					return result;
				})
				.logError();
		},
		refresh: function(params, loaded, routeMatch) {
			// console.log("view refresh : params : ", params, " - loaded : ", loaded);
			var self = this;
			params = params || this.params;
			if (params && deep.isBrowser)
				this.params = params;
			var render = function(loaded) {
				// console.log("view render : ", loaded)
				if (!loaded)
					return;
				var what = null,
					how = null,
					where = null,
					rendered = null,
					placed = null;
				if (loaded.what)
					what = loaded.what;
				if (loaded.how)
					how = loaded.how;
				if (loaded.where)
					where = loaded.where;
				what = (what && what._deep_query_node_) ? what.value : what;
				how = (how && how._deep_query_node_) ? how.value : how;
				where = (where && where._deep_query_node_) ? where.value : where;

				if (what && what._deep_ocm_)
					what = what();
				if (how && how._deep_ocm_)
					how = how();
				if (where && where._deep_ocm_)
					where = where();

				if (!what)
					what = params || {};

				placed = self.placed ? self.placed() : null;

				loaded.placed = placed;
				// console.log("render with : how : ", how, what)
				if (!how)
					return loaded;
				try {
					what._ctrl = self;
					// console.log("XHAT : ", what);
					if (routeMatch) {
						what._route = routeMatch;
						loaded.getRoute = self.getRoute = routeMatch.get;
						loaded.go = self.go = routeMatch.go;
					}
					if (typeof how === 'function')
						rendered = loaded.rendered = how.call(self, what);
					else
						rendered = loaded.rendered = how;
					if (where)
						placed = loaded.placed = where.call(self, loaded.rendered, loaded.placed);
					else
						placed = loaded.placed = null;
				} catch (e) {
					// console.log("error while rendering :  ",e);
					if (typeof self.fail === 'function')
						return self.fail(e) || e;
					return e;
				}

				if (placed && !deep.context.concurrency)
					self.placed = function() {
						return placed;
					};

				var promise = null;
				if (placed && deep.context.$) {
					//console.log("view placed : placed = ", placed);
					if (placed._deep_html_merged_) {
						var subprom = [];
						for (var i in placed) {
							if (i === '_deep_html_merged_')
								continue;
							subprom.push(self.enhance(placed[i]))
						}
						promise = deep.all(subprom);
					} else
						promise = self.enhance(placed);
				}
				// console.log("rendered : ", loaded);
				if (promise)
					return promise
						.done(function() {
							if (typeof self.done === "function") {
								var done = self.done;
								if (done._deep_ocm_)
									done = done();
								return done.call(self, loaded) || loaded;
							}
							return loaded;
						});
				if (typeof self.done === "function") {
					var done = self.done;
					if (done._deep_ocm_)
						done = done();
					return done.call(self, loaded) || loaded;
				}
				return loaded;
			};
			if (loaded)
				return render(loaded);
			return deep.when(this.load(params))
				.done(render)
				.fail(function(e) {
					if (typeof self.fail === 'function') {
						var fail = self.fail;
						if (fail._deep_ocm_)
							fail = fail();
						return fail.call(self, e) || e;
					}
				});
		},
		enhance: function(selector) {
			//console.log("enhance : ", selector);
			var promises = [];
			var self = this;
			deep.context.$(selector)
				.find("*")
				.each(function(index, element) {
					//console.log("try enhance on : ", this);
					var attr, i, len, enhancer;
					if (!deep.isBrowser) // CHEERIO specific
					{
						for (i = 0, len = this.length; i < len; ++i) {
							var e = this[i];
							if (!e.attribs)
								continue;
							for (var j in e.attribs) {
								attr = e.attribs[j];
								//console.log("attr : ",attr.name);
								if (j.indexOf("dp-") === 0 && deep.ui.View.htmlEnhancers[j]) {
									// console.log("HTML enhancer matched : ", attr.name);
									enhancer = deep.ui.View.htmlEnhancers[j];
									promises.push(enhancer.call(self, this));
									break;
								}
							}
						}
						return true;
					}

					if (!this.attributes)
						return;
					for (i = 0, len = this.attributes.length; i < len; ++i) {
						attr = this.attributes[i];
						//console.log("attr : ",attr.name);
						if (attr.name.indexOf("dp-") === 0 && deep.ui.View.htmlEnhancers[attr.name]) {
							// console.log("HTML enhancer matched : ", attr.name);
							enhancer = deep.ui.View.htmlEnhancers[attr.name];
							promises.push(enhancer.call(self, this));
							break;
						}
					}
				});
			if (promises.length === 0)
				return null;
			return deep.all(promises);
		},
		toString: function(pretty) {
			return "default view render.";
		}
	});

	deep.View = function(layer) {
		return new deep.ui.View(layer);
	};

	if (deep.isBrowser)
		deep.Chain.add("refresh", function() {
			var args = arguments;
			var self = this;
			var func = function(s, e) {
				var alls = [];
				deep.chain.each(self, function(v) {
					if (typeof v.refresh === "function")
						alls.push(v.refresh.apply(v, args));
					else
						alls.push(v);
				});
				return deep.all(alls)
					.done(function(alls) {
						if (!self._queried)
							return alls.shift();
						return alls;
					});
			};
			func._isDone_ = true;
			deep.chain.addInChain.call(self, func);
			return this;
		});

	deep.ui.View.htmlEnhancers = {
		"dp-partial": function(node) {
			//console.log("dp-partial")
			var $ = deep.context.$;
			var controller = this;
			var args = $(node).attr("dp-args") || null;
			return deep.getAll([$(node).attr("dp-partial"), args])
				.done(function(results) {
					//console.log("dp-partial get : ", results)
					var partials = results[0];
					var args = results[1];
					if (args)
						args._ctrl = controller;
					else
						args = {
							_ctrl: controller
						};
					if (typeof partials === 'function')
						$(node).html(partials(args));
					else
						$(node).html(partials);
					$(node).removeAttr("dp-partial");
					return controller.enhance(node);
				});
		},
		"dp-click": function(node) {
			if (!deep.isBrowser)
				return;
			//console.log("DPCLICK : descriptor = ", controller, "attributes : ", node);
			var $ = deep.context.$;
			var controller = this;
			var action = $(node).attr("dp-click");
			var args = $(node).attr("dp-args");
			if (args)
				args = args.split(",");
			$(node).click(function(e) {
				e.preventDefault();
				controller[action].apply(controller, args);
				return false;
			});
			//console.log("inside a dpclick tag : Action = ", action, " Args : ", args);
		},
		"dp-repeat": function(node) {
			//console.log("DP-REPEAT : descriptor = ", controller);
			var $ = deep.context.$;
			var controller = this;
			var rowNode = $(node);
			var rowHtml = deep.ui.swig(deep.jquery(rowNode));
			controller.myDatas.forEach(function(data) {
				rowNode.after(rowHtml(data));
			});
			rowNode.remove();
		},
		"dp-from": function(node) {
			var $ = deep.context.$;
			var controller = this;
			var from = $(node).attr("dp-from");
			return deep.get(from)
				.done(function(result) {
					//console.log("dp-from ", from," : get : ", result)
					if (typeof result === 'function')
						$(node).html(result({
							self: controller
						}));
					else
						$(node).html(result);
					$(node).removeAttr("dp-from");
					return controller.enhance(node);
				});
		},
		"dp-translate": function(node) {
			var $ = deep.context.$;
			var controller = this;
			var from = $(node).attr("dp-from");
			return deep.get(from)
				.done(function(result) {
					//console.log("dp-from ", from," : get : ", result)
					$(node).html(result[deep.context.language]);
					$(node).removeAttr("dp-translate");
				});
		}
	};
	return deep.ui.View;
});<|endoftext|>


<commit_before>"use strict"

var VolTextObject
var map, annotationLayer
var programmaticMapChange = false

var annotationChanges = 0
var selvol = "0"
var annotationClassesAndAppliers
var keyCodeActions

var recentLocations = []
var recentLocationsMaxLength = 10

var lastClickedElement

$(document).ready(function() {
    // This handles selection in dataTable
    var table = $('#vol_table').DataTable();

    $('#vol_table tbody').on( 'click', 'tr', function () {
        if ( $(this).hasClass('selected') ) {
            $(this).removeClass('selected');
        }
        else {
            table.$('tr.selected').removeClass('selected');
            $(this).addClass('selected');
        }
    } );

    $('#button').click( function () {
        table.row('.selected').remove().draw( false );
    } );

    $("#col2text").on("cut copy paste", function(e) {
        e.preventDefault()
    })
    // Prevent changes in a content-editable div
    $("#col2text").on("keydown", function(e) {
        e = e || window.event;
        // Allow arrow keys, home, end, pgup, pgdn
        if (e.keyCode < 33 || e.keyCode > 40)
            e.preventDefault()
        keyCodeActions.forEach(function(action) {
            if (e.keyCode == action.code)
                action.action()
        })
    })
} );

function getSelectionNodes() {
    var selectionRange = getSelectionRange()
    return getRangeNodes(selectionRange, annotationClasses)
}

function setSelectionToNode(node) {
    var range = rangy.createRange()
    range.selectNodeContents(node)
    var sel = rangy.getSelection()
    sel.setSingleRange(range)
}

var DeleteFeature = OpenLayers.Class(OpenLayers.Control, {
    initialize: function(layer, options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.layer = layer;
        this.handler = new OpenLayers.Handler.Feature(
            this, layer, {click: this.clickFeature}
        );
    },
    clickFeature: function(feature) {
        // if feature doesn't have a fid, destroy it
        if(feature.fid == undefined) {
            this.layer.destroyFeatures([feature]);
        } else {
            feature.state = OpenLayers.State.DELETE;
            this.layer.events.triggerEvent("afterfeaturemodified",
                                           {feature: feature});
            feature.renderIntent = "select";
            this.layer.drawFeature(feature);
        }
    },
    setMap: function(map) {
        this.handler.setMap(map);
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Control.DeleteFeature"
});

function getMapFeatures() {
    //var mLayers = map.layers;
    // var annLayer = map.getLayersByName("Annotations")
    var annLayer = annotationLayer

    var geoJSON = new OpenLayers.Format.GeoJSON()

    var jsonfeatsarr = annLayer.features.map(function(feature) {
        var layer_geom = feature.geometry.transform("EPSG:900913", "EPSG:4326")
        var geoJSONText = geoJSON.write(layer_geom)
        return geoJSONText
    })
    var jsonfeats = jsonfeatsarr.join("@@")
    // window.alert(jsonfeats)
    return jsonfeats
}

function jsonToMapFeatures(jsonstr) {
    var geoJSON = new OpenLayers.Format.GeoJSON()

    var jsonfeats = jsonstr.split("@@")
    // debugger
    var feats = jsonfeats.map(function(jsonfeat) {
        var geom = geoJSON.read(jsonfeat, "Geometry")
        var transformedGeom = geom.transform("EPSG:4326", "EPSG:900913")
        return new OpenLayers.Feature.Vector(transformedGeom)
    })
    return feats
}

function getStoredMapFeatures(node) {
    return $.data(node, "features")
}

function setStoredMapFeatures(node, feats) {
    $.data(node, "features", feats)
}

// Store the specified GeoJSON map features in the ranges associated with the selection.
// Return the range nodes in the selection.
function addMapFeaturesToSelection(jsonfeats) {
    var rangenodes = getSelectionNodes()
    console.log(rangenodes)
    rangenodes.forEach(function(node) {
        setStoredMapFeatures(node, jsonfeats)
    })
    return rangenodes
}

function doProgrammaticMapChange(cb) {
    try {
        programmaticMapChange = true
        cb()
    } finally {
        programmaticMapChange = false
    }
}

function destroyMapFeatures() {
    doProgrammaticMapChange(function() { annotationLayer.destroyFeatures() })
}

function addMapFeatures(feats) {
    doProgrammaticMapChange(function() { annotationLayer.addFeatures(feats) })
}

function displayMapFeatures(jsonfeats) {
    destroyMapFeatures()
    if (jsonfeats)
        addMapFeatures(jsonToMapFeatures(jsonfeats))
}

function populateRecentLocations() {
    var htmlarr = []
    for (var i = recentLocations.length - 1; i >= 0; i--) {
        var recentLoc = recentLocations[i]
        var html = '
' + '⒳	<input value="' +
                     recentLoc.html + '" onchange="locChange(event)" data-index="' + i +
                     '">	Location
'
        console.log(html)
        htmlarr.push(html)
    }
    $('#recentlocs').html(htmlarr.join("\n"))
}

/* Add an element to the recentLocations list with the specified HTML and
 * GeoJSON features. */
function addToRecentLocations(html, jsonfeats) {
    // If the HTML text isn't in the list, add an element to the end.
    // Else replace the current element.
    var curIndex = recentLocations.map(function(obj) { return obj.html }).indexOf(html)
    if (curIndex === -1) {
        if (recentLocations.length >= recentLocationsMaxLength)
            recentLocations = recentLocations.slice(1)
        recentLocations.push({html: html, jsonfeats: jsonfeats})
    } else {
        recentLocations[curIndex] = {html: html, jsonfeats: jsonfeats}
    }
    populateRecentLocations()
}

function locChange(e) {
    console.log(e.target.value)
    recentLocations[$(e.target).attr('data-index')].html = e.target.value
}

function locClick(e) {
    var jsonfeats = unescape($(e.target).attr('data-jsonfeats'))
    console.log(jsonfeats)
    displayMapFeatures(jsonfeats)
    if (lastClickedElement)
        setSelectionToNode(lastClickedElement)
    addMapFeaturesToSelection(jsonfeats)
}

function zoomFeatures() {
    window.alert("Zoom Action Needed")
}

// Save annotations in a serialized format.
function saveVolumeAnnotations() {
    // Fetch annotations
    var annotations = getAnnotations(annotationClasses)
    // Convert to an array of serialized annotations in the form "CLASS$START$END".
    var serialAnnotations = annotations.map(function(ann) {
        var jsonmapfeats = getStoredMapFeatures(ann.node) || ""
        return ann.node.className + "$" + ann.start + "$" + ann.end + "$" + jsonmapfeats
    })
    // Join to a single serialized string
    var serialString = serialAnnotations.join("|")
    var base64 = utf8ToB64(serialString);
    var parse_file = new Parse.File((annotUser + "-" + selvol +".txt"), { base64: base64 });
    // Save to Parse. First look for an existing entry for the user and volume.
    // If found, update it. Else create a new entry.
    var query = new Parse.Query(SpansObject)
    query.equalTo("user", annotUser)
    query.equalTo("vol", selvol)
    query.first().then(function(existing) {
        if (existing) {
            existing.set("spans", parse_file)
            return existing.save()
        } else {
            var spansObject = new SpansObject()
            return spansObject.save({"user":annotUser, "vol":selvol, "spans":parse_file})
        }
    }, savefailure("finding existing entry")
    ).then(savesuccess(function() {
        annotationChanges = 0
        logMessage("Saved " + annotations.length + " annotations")
    }),
        savefailure("saving new or updating existing entry"))
}

// Called from HTML. Save annotations. If saved successfully, reset list of
// article changes.
function saveAnnotations() {
    if (annotUser != "Default") {
        saveVolumeAnnotations()
    } else {
        logMessage("Please select a non-default Annotator Name prior prior to saving")
    }
}

// Load volume annotations
function loadVolumeAnnotations(results) {
    var textDivNode = getTextNode()
    textDivNode.normalize()
    var textNode = textDivNode.childNodes[0]
    httpGet(results[0].get("spans").url(), function(spansText) {
        var spansSerialized = spansText.split("|")
        var spans = spansSerialized.map(function(span) {
            var splitSpan = span.split("$")
            var className = splitSpan[0]
            var start = splitSpan[1]
            var end = splitSpan[2]
            var jsonmapfeats = splitSpan[3]
            return {start: start, end: end, className: className, jsonmapfeats: jsonmapfeats}
        })
        spans.sort(function(a, b) { return b.start - a.start })
        for (var i = 0; i < spans.length; i++) {
            var span = spans[i]
            if (span.start > textNode.length || span.end > textNode.length) {
                console.log("Skipped span [" + span.start + "," +
                    span.end + "] because > " + textNode.length)
            } else {
                var range = rangy.createRange()
                range.setStartAndEnd(textNode, span.start, span.end)
                annotationClassesAndAppliers.forEach(function(ca) {
                    if (span.className == ca.clazz) {
                        ca.applier.applyToRange(range)
                    }
                })
                getRangeNodes(range, annotationClasses).forEach(function(node) {
                    if (span.jsonmapfeats)
                        setStoredMapFeatures(node, span.jsonmapfeats)
                })
            }
        }
    })
}

function closeDialog(node) {
    $(node).dialog("close")
}

function checkVol(tableSelector) {
    if (annotUser != "Default") {
        var table = $(tableSelector).DataTable()
        var ret = table.$('tr.selected')
        if (ret.length > 0) {
            var newvol = table.$('tr.selected').find('td:first').text()
            if (annotationChanges > 0) {
                $("
Do you want to save the existing annotations?
").dialog({
                    resizable: false,
                    modal: true,
                    buttons: {
                        "Yes": function() {
                            saveAnnotations()
                            loadVolumeText(newvol, SpansObject)
                            closeDialog(this)
                        },
                        "No": function() {
                            loadVolumeText(newvol, SpansObject)
                            closeDialog(this)
                        },
                        "Cancel": function() {
                            console.log("Canceled")
                            window.alert("FIXME: We should set the visibly selected volume to the old one")
                            closeDialog(this)
                        }
                    }
                })
            }
            loadVolumeText(newvol, SpansObject)
        }
    } else {
        logMessage("Please select a non-default Annotator name prior to loading a volume")
    }
}

function nameChangeAnnotator() {
    var el = document.getElementById("selectUserAnnotator");
    annotUser = el.options[el.selectedIndex].innerHTML;
}

function removeAnnotations() {
    destroyMapFeatures()
    annotationClassesAndAppliers.forEach(function(ca) {
        ca.unapplier.undoToRange(makeRange(document.body))
    })
    annotationChanges = 0
}

function removeAnnotation() {
    var selectionRange = getSelectionRange()
    if (overlapsAnnotation(selectionRange, true, annotationClasses))
        logMessage("Selection contains part of an annotation")
    else {
        annotationClassesAndAppliers.forEach(function(ca) {
            ca.unapplier.undoToSelection()
        })
    }
}

function spanClick(element) {
    //window.alert("Clicked inside article")
    setSelectionToNode(element)
    lastClickedElement = element
    var jsonfeats = getStoredMapFeatures(element)
    // alert("GeoJSON: " + jsonfeats)
    displayMapFeatures(jsonfeats)
}

function annotationFeatureChanged(event) {
    if (!programmaticMapChange) {
        var jsonfeats = getMapFeatures()
        var rangenodes = addMapFeaturesToSelection(jsonfeats)
        if (rangenodes.length > 0) {
            var text = rangenodes[0].innerHTML.substring(0, 20)
            addToRecentLocations(text, jsonfeats)
        }
    }
    // var bounds = event.feature.geometry.getBounds();
    // var answer = "bottom: " + bounds.bottom + "\n";
    // answer += "left: " + bounds.left + "\n";
    // answer += "right: " + bounds.right + "\n";
    // answer += "top: " + bounds.top + "\n";
    // alert("Feature modified: " + answer);
}

function annotationFeatureAdded(event) {
    console.log("annotationFeatureAdded " + event)
    annotationFeatureChanged(event)
}

function annotationFeatureModified(event) {
    console.log("annotationFeatureModified " + event)
    annotationFeatureChanged(event)
}

function annotationFeatureRemoved(event) {
    console.log("annotationFeatureRemoved " + event)
    annotationFeatureChanged(event)
}

function commonMapInit() {
    var extent = new OpenLayers.Bounds(
        -12003508, 3009847, -9005759, 6557774
    );

    Parse.initialize("Dxi3BvGT3mHiDC7B1YjeEuiUQKtWIeQNofT5FIIx", "QG352rxcZvLrYeV4jOCsIZvM8mIeQyhvHzDNINAb");

    VolTextObject = Parse.Object.extend("VolumeText");

    rangy.init();

    var table = $('#vol_table').DataTable()
    var rows = getVolTableRows(table)

    map = new OpenLayers.Map('map', {
        projection: new OpenLayers.Projection("EPSG:900913"),
        displayProjection: new OpenLayers.Projection("EPSG:4326"),
        controls: [
            new OpenLayers.Control.PanZoom(),
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.MousePosition({prefix: 'Long: ', separator: '  Lat: ', numDigits: 3, emptyString:''})
        ]
    });

    var gphy = new OpenLayers.Layer.Google(
        "Google Streets"
    );

    var saveStrategy = new OpenLayers.Strategy.Save();

    annotationLayer = new OpenLayers.Layer.Vector("Annotations", {
        strategies: [new OpenLayers.Strategy.BBOX(), saveStrategy],
        projection: new OpenLayers.Projection("EPSG:4326"),
        protocol : new OpenLayers.Protocol.HTTP({
                url : 'polygons.geojson',
                format : new OpenLayers.Format.GeoJSON()
            })
    })

    map.addLayers([gphy, annotationLayer]);

    annotationLayer.events.on({
        featureadded: annotationFeatureAdded,
        featuremodified: annotationFeatureModified,
        featureremoved: annotationFeatureRemoved
    })

    var panel = new OpenLayers.Control.Panel({
        displayClass: 'customEditingToolbar',
        allowDepress: true
    });

    var draw = new OpenLayers.Control.DrawFeature(
        annotationLayer, OpenLayers.Handler.Polygon,
        {
            title: "Draw Feature",
            displayClass: "olControlDrawFeaturePolygon",
            multi: true
        }
    );

    var edit = new OpenLayers.Control.ModifyFeature(annotationLayer, {
        title: "Modify Feature",
        displayClass: "olControlModifyFeature"
    });

    var del = new DeleteFeature(annotationLayer, {title: "Delete Feature"});

    var save = new OpenLayers.Control.Button({
        title: "Save Changes",
        trigger: function() {
            if(edit.feature) {
                edit.selectControl.unselectAll();
            }
            saveStrategy.save();
            logMessage("Done Saving")
        },
        displayClass: "olControlSaveFeatures"
    });

    panel.addControls([save, del, edit, draw]);
    map.addControl(panel);
    map.zoomToExtent(extent, true);
}

// Set 4-space indentation for vi
// vi:sw=4
<commit_msg>Zoom feature now functional<commit_after>"use strict"

var VolTextObject
var map, annotationLayer
var programmaticMapChange = false

var annotationChanges = 0
var selvol = "0"
var annotationClassesAndAppliers
var keyCodeActions

var recentLocations = []
var recentLocationsMaxLength = 10

var lastClickedElement

$(document).ready(function() {
    // This handles selection in dataTable
    var table = $('#vol_table').DataTable();

    $('#vol_table tbody').on( 'click', 'tr', function () {
        if ( $(this).hasClass('selected') ) {
            $(this).removeClass('selected');
        }
        else {
            table.$('tr.selected').removeClass('selected');
            $(this).addClass('selected');
        }
    } );

    $('#button').click( function () {
        table.row('.selected').remove().draw( false );
    } );

    $("#col2text").on("cut copy paste", function(e) {
        e.preventDefault()
    })
    // Prevent changes in a content-editable div
    $("#col2text").on("keydown", function(e) {
        e = e || window.event;
        // Allow arrow keys, home, end, pgup, pgdn
        if (e.keyCode < 33 || e.keyCode > 40)
            e.preventDefault()
        keyCodeActions.forEach(function(action) {
            if (e.keyCode == action.code)
                action.action()
        })
    })
} );

function getSelectionNodes() {
    var selectionRange = getSelectionRange()
    return getRangeNodes(selectionRange, annotationClasses)
}

function setSelectionToNode(node) {
    var range = rangy.createRange()
    range.selectNodeContents(node)
    var sel = rangy.getSelection()
    sel.setSingleRange(range)
}

var DeleteFeature = OpenLayers.Class(OpenLayers.Control, {
    initialize: function(layer, options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.layer = layer;
        this.handler = new OpenLayers.Handler.Feature(
            this, layer, {click: this.clickFeature}
        );
    },
    clickFeature: function(feature) {
        // if feature doesn't have a fid, destroy it
        if(feature.fid == undefined) {
            this.layer.destroyFeatures([feature]);
        } else {
            feature.state = OpenLayers.State.DELETE;
            this.layer.events.triggerEvent("afterfeaturemodified",
                                           {feature: feature});
            feature.renderIntent = "select";
            this.layer.drawFeature(feature);
        }
    },
    setMap: function(map) {
        this.handler.setMap(map);
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Control.DeleteFeature"
});

function getMapFeatures() {
    //var mLayers = map.layers;
    // var annLayer = map.getLayersByName("Annotations")
    var annLayer = annotationLayer

    var geoJSON = new OpenLayers.Format.GeoJSON()

    var jsonfeatsarr = annLayer.features.map(function(feature) {
        var layer_geom = feature.geometry.transform("EPSG:900913", "EPSG:4326")
        var geoJSONText = geoJSON.write(layer_geom)
        return geoJSONText
    })
    var jsonfeats = jsonfeatsarr.join("@@")
    // window.alert(jsonfeats)
    return jsonfeats
}

function jsonToMapFeatures(jsonstr) {
    var geoJSON = new OpenLayers.Format.GeoJSON()

    var jsonfeats = jsonstr.split("@@")
    // debugger
    var feats = jsonfeats.map(function(jsonfeat) {
        var geom = geoJSON.read(jsonfeat, "Geometry")
        var transformedGeom = geom.transform("EPSG:4326", "EPSG:900913")
        return new OpenLayers.Feature.Vector(transformedGeom)
    })
    return feats
}

function getStoredMapFeatures(node) {
    return $.data(node, "features")
}

function setStoredMapFeatures(node, feats) {
    $.data(node, "features", feats)
}

// Store the specified GeoJSON map features in the ranges associated with the selection.
// Return the range nodes in the selection.
function addMapFeaturesToSelection(jsonfeats) {
    var rangenodes = getSelectionNodes()
    console.log(rangenodes)
    rangenodes.forEach(function(node) {
        setStoredMapFeatures(node, jsonfeats)
    })
    return rangenodes
}

function doProgrammaticMapChange(cb) {
    try {
        programmaticMapChange = true
        cb()
    } finally {
        programmaticMapChange = false
    }
}

function destroyMapFeatures() {
    doProgrammaticMapChange(function() { annotationLayer.destroyFeatures() })
}

function addMapFeatures(feats) {
    doProgrammaticMapChange(function() { annotationLayer.addFeatures(feats) })
}

function displayMapFeatures(jsonfeats) {
    destroyMapFeatures()
    if (jsonfeats)
        addMapFeatures(jsonToMapFeatures(jsonfeats))
}

function populateRecentLocations() {
    var htmlarr = []
    for (var i = recentLocations.length - 1; i >= 0; i--) {
        var recentLoc = recentLocations[i]
        var html = '
' + '⒳	<input value="' +
                     recentLoc.html + '" onchange="locChange(event)" data-index="' + i +
                     '">	Location
'
        console.log(html)
        htmlarr.push(html)
    }
    $('#recentlocs').html(htmlarr.join("\n"))
}

/* Add an element to the recentLocations list with the specified HTML and
 * GeoJSON features. */
function addToRecentLocations(html, jsonfeats) {
    // If the HTML text isn't in the list, add an element to the end.
    // Else replace the current element.
    var curIndex = recentLocations.map(function(obj) { return obj.html }).indexOf(html)
    if (curIndex === -1) {
        if (recentLocations.length >= recentLocationsMaxLength)
            recentLocations = recentLocations.slice(1)
        recentLocations.push({html: html, jsonfeats: jsonfeats})
    } else {
        recentLocations[curIndex] = {html: html, jsonfeats: jsonfeats}
    }
    populateRecentLocations()
}

function locChange(e) {
    console.log(e.target.value)
    recentLocations[$(e.target).attr('data-index')].html = e.target.value
}

function locClick(e) {
    var jsonfeats = unescape($(e.target).attr('data-jsonfeats'))
    console.log(jsonfeats)
    displayMapFeatures(jsonfeats)
    if (lastClickedElement)
        setSelectionToNode(lastClickedElement)
    addMapFeaturesToSelection(jsonfeats)
}

function zoomFeatures() {
    var bounds = annotationLayer.getDataExtent().transform("EPSG:4326", "EPSG:900913")
    //console.log(bounds)
    map.zoomToExtent(bounds)
}

// Save annotations in a serialized format.
function saveVolumeAnnotations() {
    // Fetch annotations
    var annotations = getAnnotations(annotationClasses)
    // Convert to an array of serialized annotations in the form "CLASS$START$END".
    var serialAnnotations = annotations.map(function(ann) {
        var jsonmapfeats = getStoredMapFeatures(ann.node) || ""
        return ann.node.className + "$" + ann.start + "$" + ann.end + "$" + jsonmapfeats
    })
    // Join to a single serialized string
    var serialString = serialAnnotations.join("|")
    var base64 = utf8ToB64(serialString);
    var parse_file = new Parse.File((annotUser + "-" + selvol +".txt"), { base64: base64 });
    // Save to Parse. First look for an existing entry for the user and volume.
    // If found, update it. Else create a new entry.
    var query = new Parse.Query(SpansObject)
    query.equalTo("user", annotUser)
    query.equalTo("vol", selvol)
    query.first().then(function(existing) {
        if (existing) {
            existing.set("spans", parse_file)
            return existing.save()
        } else {
            var spansObject = new SpansObject()
            return spansObject.save({"user":annotUser, "vol":selvol, "spans":parse_file})
        }
    }, savefailure("finding existing entry")
    ).then(savesuccess(function() {
        annotationChanges = 0
        logMessage("Saved " + annotations.length + " annotations")
    }),
        savefailure("saving new or updating existing entry"))
}

// Called from HTML. Save annotations. If saved successfully, reset list of
// article changes.
function saveAnnotations() {
    if (annotUser != "Default") {
        saveVolumeAnnotations()
    } else {
        logMessage("Please select a non-default Annotator Name prior prior to saving")
    }
}

// Load volume annotations
function loadVolumeAnnotations(results) {
    var textDivNode = getTextNode()
    textDivNode.normalize()
    var textNode = textDivNode.childNodes[0]
    httpGet(results[0].get("spans").url(), function(spansText) {
        var spansSerialized = spansText.split("|")
        var spans = spansSerialized.map(function(span) {
            var splitSpan = span.split("$")
            var className = splitSpan[0]
            var start = splitSpan[1]
            var end = splitSpan[2]
            var jsonmapfeats = splitSpan[3]
            return {start: start, end: end, className: className, jsonmapfeats: jsonmapfeats}
        })
        spans.sort(function(a, b) { return b.start - a.start })
        for (var i = 0; i < spans.length; i++) {
            var span = spans[i]
            if (span.start > textNode.length || span.end > textNode.length) {
                console.log("Skipped span [" + span.start + "," +
                    span.end + "] because > " + textNode.length)
            } else {
                var range = rangy.createRange()
                range.setStartAndEnd(textNode, span.start, span.end)
                annotationClassesAndAppliers.forEach(function(ca) {
                    if (span.className == ca.clazz) {
                        ca.applier.applyToRange(range)
                    }
                })
                getRangeNodes(range, annotationClasses).forEach(function(node) {
                    if (span.jsonmapfeats)
                        setStoredMapFeatures(node, span.jsonmapfeats)
                })
            }
        }
    })
}

function closeDialog(node) {
    $(node).dialog("close")
}

function checkVol(tableSelector) {
    if (annotUser != "Default") {
        var table = $(tableSelector).DataTable()
        var ret = table.$('tr.selected')
        if (ret.length > 0) {
            var newvol = table.$('tr.selected').find('td:first').text()
            if (annotationChanges > 0) {
                $("
Do you want to save the existing annotations?
").dialog({
                    resizable: false,
                    modal: true,
                    buttons: {
                        "Yes": function() {
                            saveAnnotations()
                            loadVolumeText(newvol, SpansObject)
                            closeDialog(this)
                        },
                        "No": function() {
                            loadVolumeText(newvol, SpansObject)
                            closeDialog(this)
                        },
                        "Cancel": function() {
                            console.log("Canceled")
                            window.alert("FIXME: We should set the visibly selected volume to the old one")
                            closeDialog(this)
                        }
                    }
                })
            }
            loadVolumeText(newvol, SpansObject)
        }
    } else {
        logMessage("Please select a non-default Annotator name prior to loading a volume")
    }
}

function nameChangeAnnotator() {
    var el = document.getElementById("selectUserAnnotator");
    annotUser = el.options[el.selectedIndex].innerHTML;
}

function removeAnnotations() {
    destroyMapFeatures()
    annotationClassesAndAppliers.forEach(function(ca) {
        ca.unapplier.undoToRange(makeRange(document.body))
    })
    annotationChanges = 0
}

function removeAnnotation() {
    var selectionRange = getSelectionRange()
    if (overlapsAnnotation(selectionRange, true, annotationClasses))
        logMessage("Selection contains part of an annotation")
    else {
        annotationClassesAndAppliers.forEach(function(ca) {
            ca.unapplier.undoToSelection()
        })
    }
}

function spanClick(element) {
    //window.alert("Clicked inside article")
    setSelectionToNode(element)
    lastClickedElement = element
    var jsonfeats = getStoredMapFeatures(element)
    // alert("GeoJSON: " + jsonfeats)
    displayMapFeatures(jsonfeats)
}

function annotationFeatureChanged(event) {
    if (!programmaticMapChange) {
        var jsonfeats = getMapFeatures()
        var rangenodes = addMapFeaturesToSelection(jsonfeats)
        if (rangenodes.length > 0) {
            var text = rangenodes[0].innerHTML.substring(0, 20)
            addToRecentLocations(text, jsonfeats)
        }
    }
    // var bounds = event.feature.geometry.getBounds();
    // var answer = "bottom: " + bounds.bottom + "\n";
    // answer += "left: " + bounds.left + "\n";
    // answer += "right: " + bounds.right + "\n";
    // answer += "top: " + bounds.top + "\n";
    // alert("Feature modified: " + answer);
}

function annotationFeatureAdded(event) {
    console.log("annotationFeatureAdded " + event)
    annotationFeatureChanged(event)
}

function annotationFeatureModified(event) {
    console.log("annotationFeatureModified " + event)
    annotationFeatureChanged(event)
}

function annotationFeatureRemoved(event) {
    console.log("annotationFeatureRemoved " + event)
    annotationFeatureChanged(event)
}

function commonMapInit() {
    var extent = new OpenLayers.Bounds(
        -12003508, 3009847, -9005759, 6557774
    );

    Parse.initialize("Dxi3BvGT3mHiDC7B1YjeEuiUQKtWIeQNofT5FIIx", "QG352rxcZvLrYeV4jOCsIZvM8mIeQyhvHzDNINAb");

    VolTextObject = Parse.Object.extend("VolumeText");

    rangy.init();

    var table = $('#vol_table').DataTable()
    var rows = getVolTableRows(table)

    map = new OpenLayers.Map('map', {
        projection: new OpenLayers.Projection("EPSG:900913"),
        displayProjection: new OpenLayers.Projection("EPSG:4326"),
        controls: [
            new OpenLayers.Control.PanZoom(),
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.MousePosition({prefix: 'Long: ', separator: '  Lat: ', numDigits: 3, emptyString:''})
        ]
    });

    var gphy = new OpenLayers.Layer.Google(
        "Google Streets"
    );

    var saveStrategy = new OpenLayers.Strategy.Save();

    annotationLayer = new OpenLayers.Layer.Vector("Annotations", {
        strategies: [new OpenLayers.Strategy.BBOX(), saveStrategy],
        projection: new OpenLayers.Projection("EPSG:4326"),
        protocol : new OpenLayers.Protocol.HTTP({
                url : 'polygons.geojson',
                format : new OpenLayers.Format.GeoJSON()
            })
    })

    map.addLayers([gphy, annotationLayer]);

    annotationLayer.events.on({
        featureadded: annotationFeatureAdded,
        featuremodified: annotationFeatureModified,
        featureremoved: annotationFeatureRemoved
    })

    var panel = new OpenLayers.Control.Panel({
        displayClass: 'customEditingToolbar',
        allowDepress: true
    });

    var draw = new OpenLayers.Control.DrawFeature(
        annotationLayer, OpenLayers.Handler.Polygon,
        {
            title: "Draw Feature",
            displayClass: "olControlDrawFeaturePolygon",
            multi: true
        }
    );

    var edit = new OpenLayers.Control.ModifyFeature(annotationLayer, {
        title: "Modify Feature",
        displayClass: "olControlModifyFeature"
    });

    var del = new DeleteFeature(annotationLayer, {title: "Delete Feature"});

    var save = new OpenLayers.Control.Button({
        title: "Save Changes",
        trigger: function() {
            if(edit.feature) {
                edit.selectControl.unselectAll();
            }
            saveStrategy.save();
            logMessage("Done Saving")
        },
        displayClass: "olControlSaveFeatures"
    });

    panel.addControls([save, del, edit, draw]);
    map.addControl(panel);
    map.zoomToExtent(extent, true);
}

// Set 4-space indentation for vi
// vi:sw=4
<|endoftext|>