<commit_before>(function() {
    "use strict";
    /* global myApp */
    myApp.factory("AppsService", [ "ResourcesLoader", "INSTALL_DIRECTORY", "TEMP_DIRECTORY", "APPS_JSON", "METADATA_JSON", function(ResourcesLoader, INSTALL_DIRECTORY, TEMP_DIRECTORY, APPS_JSON, METADATA_JSON) {

        var platformId = cordova.require("cordova/platform").id;
        // downloaders that know how to download from certain patterns.
        // Eg: The KnownExtensionDownloader MAY know how to download from any uri's that end in known extensions
        var downloadHandlers = [];
        // handlers that have registered to unpack certain extensions during the installation of an app
        var extensionHandlers = {};
        // functions to run before launching an app
        var preLaunchHooks = [];

        function grabExtensionFromUri(uri) {
            var lastSegment = uri.split("#")[0].split("?")[0].split("/").pop();
            var dotLocation = lastSegment.lastIndexOf(".");
            var extension = (dotLocation !== -1)? lastSegment.substring(dotLocation + 1) : "";
            return extension;
        }

        function addNewAppFromPattern(appName, appSourcePattern) {
            var _fullFilePath;

            return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName)
            .then(function(){
                for(var i = 0; i < downloadHandlers.length; i++){
                    if(downloadHandlers[i].handler.canHandleSourcePattern(appSourcePattern)){
                        return downloadHandlers[i].handler.downloadFromPattern(appName, appSourcePattern, TEMP_DIRECTORY);
                    }
                }
                throw new Error("App Harness does not know how to install an app from the pattern: " + appSourcePattern);
            })
            .then(function(fullFilePath){
                _fullFilePath = fullFilePath;
                return ResourcesLoader.ensureDirectoryExists(INSTALL_DIRECTORY + appName);
            })
            .then(function(directoryPath){
                var extension = grabExtensionFromUri(appSourcePattern);
                if(!extensionHandlers[extension]) {
                    throw new Error("No handler for extension " + extension + " found");
                }
                return extensionHandlers[extension].extractPackageToDirectory(_fullFilePath, directoryPath);
            })
            .then(function(){
                return registerApp(appName, "pattern", appSourcePattern);
            });
        }

        function registerApp(appName, appSource, appSourcePattern) {
            return ResourcesLoader.readJSONFileContents(APPS_JSON)
            .then(function(result){
                result.installedApps = result.installedApps || [];
                result.installedApps.push({
                    "Name" :  appName,
                    "Source" : appSource,
                    "Data" : appSourcePattern,
                    "Installed" : (new Date()).toLocaleString()
                });
                return ResourcesLoader.writeJSONFileContents(APPS_JSON, result);
            });
        }

        function cleanPath(path){
            if(path.indexOf("file://") === 0){
                path = path.substring("file://".length);
            }
            // remove trailing slash
            return (path.substring(path.length - 1) === "/") ? path.substring(0, path.length - 1) : path;
        }

        function isPathAbsolute(path){
            return (path.match(/^[a-zA-Z0-9]+:/) != null);
        }

        function getAppStartPageFromConfig(configFile, appBaseDirectory) {
            configFile = cleanPath(configFile);
            appBaseDirectory = "file://" + cleanPath(appBaseDirectory);

            return ResourcesLoader.readFileContents(configFile)
            .then(function(contents){
                if(!contents) {
                    throw new Error("Config file is empty. Unable to find a start page for your app.");
                } else {
                    var startLocation = appBaseDirectory + "/index.html";
                    var parser = new DOMParser();
                    var xmlDoc = parser.parseFromString(contents, "text/xml");
                    var els = xmlDoc.getElementsByTagName("content");

                    if(els.length > 0) {
                        // go through all "content" elements looking for the "src" attribute in reverse order
                        for(var i = els.length - 1; i >= 0; i--) {
                            var el = els[i];
                            var srcValue = el.getAttribute("src");
                            if(srcValue) {
                                if(isPathAbsolute(srcValue)) {
                                    startLocation = srcValue;
                                } else {
                                    srcValue = srcValue.charAt(0) === "/" ? srcValue.substring(1) : srcValue;
                                    startLocation = appBaseDirectory + "/" + srcValue;
                                }
                                break;
                            }
                        }
                    }

                    return startLocation;
                }
            });
        }

        function removeApp(appName){
            var entry;
            return ResourcesLoader.ensureDirectoryExists(APPS_JSON)
            .then(function() {
                return ResourcesLoader.readJSONFileContents(APPS_JSON);
            })
            .then(function(result){
                result.installedApps = result.installedApps || [];

                for(var i = 0; i < result.installedApps.length; i++){
                    if(result.installedApps[i].Name === appName) {
                        entry = result.installedApps.splice(i, 1)[0];
                        break;
                    }
                }

                if(!entry) {
                    throw new Error("The app " + appName + " was not found.");
                }

                return ResourcesLoader.writeJSONFileContents(APPS_JSON, result);
            })
            .then(function(){
                return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName);
            })
            .then(function(){
                return entry;
            });
        }

        return {
            //return promise with the array of apps
            getAppsList : function(getFullEntries) {
                return ResourcesLoader.ensureDirectoryExists(APPS_JSON)
                .then(function() {
                    return ResourcesLoader.readJSONFileContents(APPS_JSON);
                })
                .then(function(result){
                    result.installedApps = result.installedApps || [];
                    var newAppsList = [];

                    for(var i = 0; i < result.installedApps.length; i++){
                        if(getFullEntries) {
                            newAppsList.push(result.installedApps[i]);
                        } else {
                            newAppsList.push(result.installedApps[i].Name);
                        }
                    }

                    return newAppsList;
                });
            },

            launchApp : function(appName) {
                var platformWWWLocation;
                var startLocation;
                return ResourcesLoader.readJSONFileContents(METADATA_JSON)
                .then(function(settings){
                    settings = settings || {};
                    settings.lastLaunched = appName;
                    return ResourcesLoader.writeJSONFileContents(METADATA_JSON, settings);
                })
                .then(function(){
                    return ResourcesLoader.getFullFilePath(INSTALL_DIRECTORY + appName + "/" + platformId);
                })
                .then(function(platformLocation){
                    platformWWWLocation = platformLocation + "/www/";
                    return getAppStartPageFromConfig(platformLocation + "/config.xml", platformWWWLocation);
                })
                .then(function(_startLocation){
                    startLocation = _startLocation;
                    var promises = [];
                    for (var i = preLaunchHooks.length - 1; i >= 0; i--) {
                        promises.push(preLaunchHooks[i](appName, platformWWWLocation));
                    }
                    return Q.all(promises);
                })
                .then(function() {
                    window.location = startLocation;
                });
            },

            addAppFromPattern : function(appName, appSourcePattern) {
                return this.getAppsList(false /* App names only */)
                .then(function(appsList){
                    if(appsList.indexOf(appName) !== -1) {
                        throw new Error("An app with this name already exists");
                    }
                    return addNewAppFromPattern(appName, appSourcePattern);
                });
            },

            uninstallApp : function(appName) {
                return removeApp(appName);
            },

            getLastRunApp : function() {
                return ResourcesLoader.readJSONFileContents(METADATA_JSON)
                .then(function(settings){
                    if(!settings || !settings.lastLaunched) {
                        throw new Error("No App has been launched yet");
                    }
                    return settings.lastLaunched;
                });
            },

            registerPatternDownloader : function(handler, priority){
                if(!handler) {
                    throw new Error("Expected handler");
                }
                if(typeof(handler.canHandleSourcePattern) !== "function") {
                    throw new Error("Expected function for bool handler.canHandleSourcePattern(string pattern) to exist");
                }
                if(typeof(handler.downloadFromPattern) !== "function") {
                    throw new Error("Expected function for (string fullFilePath or QPromise) handler.downloadFromPattern(string appName, string pattern, string tempDirectory) to exist");
                }
                if(!priority) {
                    // Assign a default priority
                    priority = 500;
                }
                var i = 0;
                var objToInsert = { "priority" : priority, "handler" : handler };
                for(i = 0; i < downloadHandlers.length; i++){
                    if(downloadHandlers[i].priority > objToInsert.priority) {
                        break;
                    }
                }
                downloadHandlers.splice(i, 0, objToInsert);
            },

            registerPackageHandler : function(extension, handler) {
                if(!extension) {
                    throw new Error("Expcted extension");
                }
                if(!handler || typeof(handler.extractPackageToDirectory) !== "function") {
                    throw new Error("Expected function for void handler.extractPackageToDirectory(string fullFilePath, string directoryPath) to exist");
                }
                if(handler[extension]) {
                    throw new Error("Handler already exists for the extension: " + extension);
                }
                extensionHandlers[extension] = handler;
            },

            updateApp : function(appName){
                return removeApp(appName)
                .then(function(entry){
                    if(entry.Source === "pattern") {
                        return addNewAppFromPattern(entry.Name, entry.Data);
                    }
                });
            },

            getKnownExtensions : function() {
                return Object.keys(extensionHandlers);
            },

            addPreLaunchHook : function(handler){
                if(!handler || typeof(handler) !== "function") {
                    throw new Error("Expected (QPromise or void) function(appName, wwwLocation) for handler");
                }
                preLaunchHooks.push(handler);
            }
        };
    }]);
})();<commit_msg>Absolute url check bug fix<commit_after>(function() {
    "use strict";
    /* global myApp */
    myApp.factory("AppsService", [ "ResourcesLoader", "INSTALL_DIRECTORY", "TEMP_DIRECTORY", "APPS_JSON", "METADATA_JSON", function(ResourcesLoader, INSTALL_DIRECTORY, TEMP_DIRECTORY, APPS_JSON, METADATA_JSON) {

        var platformId = cordova.require("cordova/platform").id;
        // downloaders that know how to download from certain patterns.
        // Eg: The KnownExtensionDownloader MAY know how to download from any uri's that end in known extensions
        var downloadHandlers = [];
        // handlers that have registered to unpack certain extensions during the installation of an app
        var extensionHandlers = {};
        // functions to run before launching an app
        var preLaunchHooks = [];

        function grabExtensionFromUri(uri) {
            var lastSegment = uri.split("#")[0].split("?")[0].split("/").pop();
            var dotLocation = lastSegment.lastIndexOf(".");
            var extension = (dotLocation !== -1)? lastSegment.substring(dotLocation + 1) : "";
            return extension;
        }

        function addNewAppFromPattern(appName, appSourcePattern) {
            var _fullFilePath;

            return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName)
            .then(function(){
                for(var i = 0; i < downloadHandlers.length; i++){
                    if(downloadHandlers[i].handler.canHandleSourcePattern(appSourcePattern)){
                        return downloadHandlers[i].handler.downloadFromPattern(appName, appSourcePattern, TEMP_DIRECTORY);
                    }
                }
                throw new Error("App Harness does not know how to install an app from the pattern: " + appSourcePattern);
            })
            .then(function(fullFilePath){
                _fullFilePath = fullFilePath;
                return ResourcesLoader.ensureDirectoryExists(INSTALL_DIRECTORY + appName);
            })
            .then(function(directoryPath){
                var extension = grabExtensionFromUri(appSourcePattern);
                if(!extensionHandlers[extension]) {
                    throw new Error("No handler for extension " + extension + " found");
                }
                return extensionHandlers[extension].extractPackageToDirectory(_fullFilePath, directoryPath);
            })
            .then(function(){
                return registerApp(appName, "pattern", appSourcePattern);
            });
        }

        function registerApp(appName, appSource, appSourcePattern) {
            return ResourcesLoader.readJSONFileContents(APPS_JSON)
            .then(function(result){
                result.installedApps = result.installedApps || [];
                result.installedApps.push({
                    "Name" :  appName,
                    "Source" : appSource,
                    "Data" : appSourcePattern,
                    "Installed" : (new Date()).toLocaleString()
                });
                return ResourcesLoader.writeJSONFileContents(APPS_JSON, result);
            });
        }

        function cleanPath(path){
            if(path.indexOf("file://") === 0){
                path = path.substring("file://".length);
            }
            // remove trailing slash
            return (path.substring(path.length - 1) === "/") ? path.substring(0, path.length - 1) : path;
        }

        function isPathAbsolute(path){
            return (path.match(/^[a-z0-9+.-]+:/) != null);
        }

        function getAppStartPageFromConfig(configFile, appBaseDirectory) {
            configFile = cleanPath(configFile);
            appBaseDirectory = "file://" + cleanPath(appBaseDirectory);

            return ResourcesLoader.readFileContents(configFile)
            .then(function(contents){
                if(!contents) {
                    throw new Error("Config file is empty. Unable to find a start page for your app.");
                } else {
                    var startLocation = appBaseDirectory + "/index.html";
                    var parser = new DOMParser();
                    var xmlDoc = parser.parseFromString(contents, "text/xml");
                    var els = xmlDoc.getElementsByTagName("content");

                    if(els.length > 0) {
                        // go through all "content" elements looking for the "src" attribute in reverse order
                        for(var i = els.length - 1; i >= 0; i--) {
                            var el = els[i];
                            var srcValue = el.getAttribute("src");
                            if(srcValue) {
                                if(isPathAbsolute(srcValue)) {
                                    startLocation = srcValue;
                                } else {
                                    srcValue = srcValue.charAt(0) === "/" ? srcValue.substring(1) : srcValue;
                                    startLocation = appBaseDirectory + "/" + srcValue;
                                }
                                break;
                            }
                        }
                    }

                    return startLocation;
                }
            });
        }

        function removeApp(appName){
            var entry;
            return ResourcesLoader.ensureDirectoryExists(APPS_JSON)
            .then(function() {
                return ResourcesLoader.readJSONFileContents(APPS_JSON);
            })
            .then(function(result){
                result.installedApps = result.installedApps || [];

                for(var i = 0; i < result.installedApps.length; i++){
                    if(result.installedApps[i].Name === appName) {
                        entry = result.installedApps.splice(i, 1)[0];
                        break;
                    }
                }

                if(!entry) {
                    throw new Error("The app " + appName + " was not found.");
                }

                return ResourcesLoader.writeJSONFileContents(APPS_JSON, result);
            })
            .then(function(){
                return ResourcesLoader.deleteDirectory(INSTALL_DIRECTORY + appName);
            })
            .then(function(){
                return entry;
            });
        }

        return {
            //return promise with the array of apps
            getAppsList : function(getFullEntries) {
                return ResourcesLoader.ensureDirectoryExists(APPS_JSON)
                .then(function() {
                    return ResourcesLoader.readJSONFileContents(APPS_JSON);
                })
                .then(function(result){
                    result.installedApps = result.installedApps || [];
                    var newAppsList = [];

                    for(var i = 0; i < result.installedApps.length; i++){
                        if(getFullEntries) {
                            newAppsList.push(result.installedApps[i]);
                        } else {
                            newAppsList.push(result.installedApps[i].Name);
                        }
                    }

                    return newAppsList;
                });
            },

            launchApp : function(appName) {
                var platformWWWLocation;
                var startLocation;
                return ResourcesLoader.readJSONFileContents(METADATA_JSON)
                .then(function(settings){
                    settings = settings || {};
                    settings.lastLaunched = appName;
                    return ResourcesLoader.writeJSONFileContents(METADATA_JSON, settings);
                })
                .then(function(){
                    return ResourcesLoader.getFullFilePath(INSTALL_DIRECTORY + appName + "/" + platformId);
                })
                .then(function(platformLocation){
                    platformWWWLocation = platformLocation + "/www/";
                    return getAppStartPageFromConfig(platformLocation + "/config.xml", platformWWWLocation);
                })
                .then(function(_startLocation){
                    startLocation = _startLocation;
                    var promises = [];
                    for (var i = preLaunchHooks.length - 1; i >= 0; i--) {
                        promises.push(preLaunchHooks[i](appName, platformWWWLocation));
                    }
                    return Q.all(promises);
                })
                .then(function() {
                    window.location = startLocation;
                });
            },

            addAppFromPattern : function(appName, appSourcePattern) {
                return this.getAppsList(false /* App names only */)
                .then(function(appsList){
                    if(appsList.indexOf(appName) !== -1) {
                        throw new Error("An app with this name already exists");
                    }
                    return addNewAppFromPattern(appName, appSourcePattern);
                });
            },

            uninstallApp : function(appName) {
                return removeApp(appName);
            },

            getLastRunApp : function() {
                return ResourcesLoader.readJSONFileContents(METADATA_JSON)
                .then(function(settings){
                    if(!settings || !settings.lastLaunched) {
                        throw new Error("No App has been launched yet");
                    }
                    return settings.lastLaunched;
                });
            },

            registerPatternDownloader : function(handler, priority){
                if(!handler) {
                    throw new Error("Expected handler");
                }
                if(typeof(handler.canHandleSourcePattern) !== "function") {
                    throw new Error("Expected function for bool handler.canHandleSourcePattern(string pattern) to exist");
                }
                if(typeof(handler.downloadFromPattern) !== "function") {
                    throw new Error("Expected function for (string fullFilePath or QPromise) handler.downloadFromPattern(string appName, string pattern, string tempDirectory) to exist");
                }
                if(!priority) {
                    // Assign a default priority
                    priority = 500;
                }
                var i = 0;
                var objToInsert = { "priority" : priority, "handler" : handler };
                for(i = 0; i < downloadHandlers.length; i++){
                    if(downloadHandlers[i].priority > objToInsert.priority) {
                        break;
                    }
                }
                downloadHandlers.splice(i, 0, objToInsert);
            },

            registerPackageHandler : function(extension, handler) {
                if(!extension) {
                    throw new Error("Expcted extension");
                }
                if(!handler || typeof(handler.extractPackageToDirectory) !== "function") {
                    throw new Error("Expected function for void handler.extractPackageToDirectory(string fullFilePath, string directoryPath) to exist");
                }
                if(handler[extension]) {
                    throw new Error("Handler already exists for the extension: " + extension);
                }
                extensionHandlers[extension] = handler;
            },

            updateApp : function(appName){
                return removeApp(appName)
                .then(function(entry){
                    if(entry.Source === "pattern") {
                        return addNewAppFromPattern(entry.Name, entry.Data);
                    }
                });
            },

            getKnownExtensions : function() {
                return Object.keys(extensionHandlers);
            },

            addPreLaunchHook : function(handler){
                if(!handler || typeof(handler) !== "function") {
                    throw new Error("Expected (QPromise or void) function(appName, wwwLocation) for handler");
                }
                preLaunchHooks.push(handler);
            }
        };
    }]);
})();<|endoftext|>


<commit_before>/**
 * Pew / Project / Sprites
 */
Pew.Project.prototype.sprites = (function() {

    /**
     * Sprite object
     *
     * @param  string name
     */
    function Sprite(conf) {

        var def = {
            resource: '',
            clip: {
                w : 0,
                h : 0,
            },
            frameset: [],
        };

        this.conf       = Pew.utils.extend({}, def, conf);
        this.frames     = [];
        this.to_radians = Math.PI/180;

        // get resource if needed
        if(typeof this.conf.resource === "string") {
            this.conf.resource = Pew.project().resources.get(this.conf.resource);
        }

        if(this.conf.clip.w > 0 && this.conf.clip.h > 0) {
            this.clip(this.conf.clip.w, this.conf.clip.h);
        }

        return this;
    }

    /**
     * Cut resource into frames
     *
     * @param integer fw frame width
     * @param integer fh frame height
     */
    Sprite.prototype.clip = function(fw, fh) {

        this.frames = [];
        this.conf.clip.w = Math.round(fw);
        this.conf.clip.h = Math.round(fh);

        var w = this.conf.resource.width,
            h = this.conf.resource.height,
            cols = Math.ceil(w/fw),
            rows = Math.ceil(h/fh);

        for(var i=0;i < rows;++i) {
            for(var j=0;j < cols;++j) {
                this.frames.push({
                    x: this.conf.clip.w * j,
                    y: this.conf.clip.h * i,
                    w: fw,
                    h: fh,
                    cx: Math.round(fw/2),
                    cy: Math.round(fh/2)
                });
            }
        }
    };

    /**
     * Show all frames with their index number
     *
     * @param object  layer Pew.Layer instance
     * @param integer x
     * @param integer y
     */
    Sprite.prototype.framesIndexMap = function(layer, x, y) {

        x = x || 0;
        y = y || 0;

        var font = layer.ctx.font;
        layer.ctx.font = "12px monospace";

        for(var i=0;i<this.frames.length;++i) {

            this.drawFrame(i, layer, this.frames[i].x, this.frames[i].y);

            layer.ctx.beginPath();
            layer.ctx.globalAlpha = 0.4;
            layer.ctx.rect(this.frames[i].x, this.frames[i].y + this.frames[i].h/2 - 10, 50, 13);
            layer.ctx.fillStyle = '#fff';
            layer.ctx.fill();

            layer.ctx.globalAlpha = 1;
            layer.ctx.fillStyle = "#000";
            layer.ctx.fillText(i, this.frames[i].x + (this.frames[i].w/2), this.frames[i].y + (this.frames[i].h/2));
        }

        layer.ctx.font = font;
    };

    /**
     * Get frame
     *
     * @param  integer num frame number
     * @return object
     */
    Sprite.prototype.getFrame = function(num) {

        var f = this.frames[num];
        return f;
    };

    /**
     * Get frames with extra data
     *
     * @param  mixed num frame number
     * @return object
     */
    Sprite.prototype.getFrames = function(range) {

    };

    /**
     * Draw sprite frame
     *
     * @param  integer num
     * @param  object  layer Pew.Layer instance
     * @param  integer x
     * @param  integer y
     * @param  integer deg
     */
    Sprite.prototype.drawFrame = function(num, layer, x, y, deg) {

        var f = this.frames[num];

        if(f !== undefined) {

            var res = this.conf.resource;

            // need rotation
            if(deg !== undefined) {
                res = this.preRender(f, deg);
                layer.ctx.drawImage(res, x, y);
            }
            else {
                layer.ctx.drawImage(res,
                    f.x, f.y,
                    f.w, f.h,
                    x+f.cx, y+f.cy,
                    f.w, f.h
                );
            }
        }

        return this;
    };

    /**
     * Pre render a frame that need rotation
     *
     * @param  object  frame
     * @param  integer degree
     * @return object  Image object
     */
    Sprite.prototype.preRender = function(frame, deg) {

        var canvas = document.createElement('canvas'),
            ctx    = canvas.getContext('2d'),
            img    = new Image(),
            radian = deg * this.to_radians;

        canvas.width  = frame.w*2;
        canvas.height = frame.h*2;

        ctx.translate(frame.w, frame.h);
        ctx.rotate(radian);
        ctx.drawImage(this.conf.resource,
            frame.x, frame.y,
            frame.w, frame.h,
            frame.cx*-1, frame.cy*-1,
            frame.w, frame.h
        );

        img.src = canvas.toDataURL('image/png');
        return img;
    }

    /**
     * Create animation set
     *
     * @param  object conf
     * @return object
     */
    Sprite.prototype.createAnim = function(conf) {

        return new Animation(conf, this.frames.length);
    };

    /**
     * Animation from a custom set of frames
     *
     * @param object  conf
     * @param integer total
     */
    function Animation(conf, total) {

        var def = {
            duration: 500, //ms
            frames: [],
            loop: true,
        };

        this.conf = Pew.utils.extend({}, def, conf);

        if(this.conf.frames === 'all') {
            this.conf.frames = [];
            for(var i=0;i<total;++i) this.conf.frames.push(i);
        }

        this.ticks       = Pew.project().anim_frame.frameRate * (this.conf.duration / 1000);
        this.tick_index  = 0;
        this.cur_frame   = 0;
        this.fpt         = Math.round(this.ticks / this.conf.frames.length);
    }

    /**
     * Loop throw the set and return the current frame
     */
    Animation.prototype.loop = function() {

        if(this.tick_index > this.fpt) {
            this.tick_index = 0;
            ++this.cur_frame;
        }

        if(this.cur_frame > (this.conf.frames.length - 1)) {
            if(this.conf.loop === false) {
                --this.cur_frame;
            }
            else {
                this.cur_frame = 0;
            }
        }

        ++this.tick_index;
    };

    /**
     * Get frame
     *
     * @return integer
     */
    Animation.prototype.getFrame = function() {
        this.loop();
        return this.conf.frames[this.cur_frame];
    };

    /**
     * Public stuff
     */
    return {

        create: function(conf) {
            return new Sprite(conf);
        },
    }
})();<commit_msg>added getFrames(range)<commit_after>/**
 * Pew / Project / Sprites
 */
Pew.Project.prototype.sprites = (function() {

    /**
     * Sprite object
     *
     * @param  string name
     */
    function Sprite(conf) {

        var def = {
            resource: '',
            clip: {
                w : 0,
                h : 0,
            },
            frameset: [],
        };

        this.conf       = Pew.utils.extend({}, def, conf);
        this.frames     = [];
        this.to_radians = Math.PI/180;

        // get resource if needed
        if(typeof this.conf.resource === "string") {
            this.conf.resource = Pew.project().resources.get(this.conf.resource);
        }

        if(this.conf.clip.w > 0 && this.conf.clip.h > 0) {
            this.clip(this.conf.clip.w, this.conf.clip.h);
        }

        return this;
    }

    /**
     * Cut resource into frames
     *
     * @param integer fw frame width
     * @param integer fh frame height
     */
    Sprite.prototype.clip = function(fw, fh) {

        this.frames = [];
        this.conf.clip.w = Math.round(fw);
        this.conf.clip.h = Math.round(fh);

        var w = this.conf.resource.width,
            h = this.conf.resource.height,
            cols = Math.ceil(w/fw),
            rows = Math.ceil(h/fh);

        for(var i=0;i < rows;++i) {
            for(var j=0;j < cols;++j) {
                this.frames.push({
                    x: this.conf.clip.w * j,
                    y: this.conf.clip.h * i,
                    w: fw,
                    h: fh,
                    cx: Math.round(fw/2),
                    cy: Math.round(fh/2)
                });
            }
        }
    };

    /**
     * Show all frames with their index number
     *
     * @param object  layer Pew.Layer instance
     * @param integer x
     * @param integer y
     */
    Sprite.prototype.framesIndexMap = function(layer, x, y) {

        x = x || 0;
        y = y || 0;

        var font = layer.ctx.font;
        layer.ctx.font = "12px monospace";

        for(var i=0;i<this.frames.length;++i) {

            this.drawFrame(i, layer, this.frames[i].x, this.frames[i].y);

            layer.ctx.beginPath();
            layer.ctx.globalAlpha = 0.4;
            layer.ctx.rect(this.frames[i].x, this.frames[i].y + this.frames[i].h/2 - 10, 50, 13);
            layer.ctx.fillStyle = '#fff';
            layer.ctx.fill();

            layer.ctx.globalAlpha = 1;
            layer.ctx.fillStyle = "#000";
            layer.ctx.fillText(i, this.frames[i].x + (this.frames[i].w/2), this.frames[i].y + (this.frames[i].h/2));
        }

        layer.ctx.font = font;
    };

    /**
     * Get frame
     *
     * @param  integer num frame number
     * @return object
     */
    Sprite.prototype.getFrame = function(num) {

        var f = this.frames[num];
        return f;
    };

    /**
     * Get frames with extra data
     *
     * @param  mixed num frame number
     * @return object
     */
    Sprite.prototype.getFrames = function(range) {

        var range = Pew.utils.isRangeStr(range);
        if(range != null) {
            var r = [];
            for(var i=range[1];i <= range[2];++i) {
                r.push(this.frames[i]);
            }
            return r;
        }
        return [];
    };

    /**
     * Draw sprite frame
     *
     * @param  integer num
     * @param  object  layer Pew.Layer instance
     * @param  integer x
     * @param  integer y
     * @param  integer deg
     */
    Sprite.prototype.drawFrame = function(num, layer, x, y, deg) {

        var f = this.frames[num];

        if(f !== undefined) {

            var res = this.conf.resource;

            // need rotation
            if(deg !== undefined) {
                res = this.preRender(f, deg);
                layer.ctx.drawImage(res, x, y);
            }
            else {
                layer.ctx.drawImage(res,
                    f.x, f.y,
                    f.w, f.h,
                    x+f.cx, y+f.cy,
                    f.w, f.h
                );
            }
        }

        return this;
    };

    /**
     * Pre render a frame that need rotation
     *
     * @param  object  frame
     * @param  integer degree
     * @return object  Image object
     */
    Sprite.prototype.preRender = function(frame, deg) {

        var canvas = document.createElement('canvas'),
            ctx    = canvas.getContext('2d'),
            img    = new Image(),
            radian = deg * this.to_radians;

        canvas.width  = frame.w*2;
        canvas.height = frame.h*2;

        ctx.translate(frame.w, frame.h);
        ctx.rotate(radian);
        ctx.drawImage(this.conf.resource,
            frame.x, frame.y,
            frame.w, frame.h,
            frame.cx*-1, frame.cy*-1,
            frame.w, frame.h
        );

        img.src = canvas.toDataURL('image/png');
        return img;
    }

    /**
     * Create animation set
     *
     * @param  object conf
     * @return object
     */
    Sprite.prototype.createAnim = function(conf) {

        return new Animation(conf, this.frames.length);
    };

    /**
     * Animation from a custom set of frames
     *
     * @param object  conf
     * @param integer total
     */
    function Animation(conf, total) {

        var def = {
            duration: 500, //ms
            frames: [],
            loop: true,
        };

        this.conf = Pew.utils.extend({}, def, conf);

        if(this.conf.frames === 'all') {
            this.conf.frames = [];
            for(var i=0;i<total;++i) this.conf.frames.push(i);
        }

        this.ticks       = Pew.project().anim_frame.frameRate * (this.conf.duration / 1000);
        this.tick_index  = 0;
        this.cur_frame   = 0;
        this.fpt         = Math.round(this.ticks / this.conf.frames.length);
    }

    /**
     * Loop throw the set and return the current frame
     */
    Animation.prototype.loop = function() {

        if(this.tick_index > this.fpt) {
            this.tick_index = 0;
            ++this.cur_frame;
        }

        if(this.cur_frame > (this.conf.frames.length - 1)) {
            if(this.conf.loop === false) {
                --this.cur_frame;
            }
            else {
                this.cur_frame = 0;
            }
        }

        ++this.tick_index;
    };

    /**
     * Get frame
     *
     * @return integer
     */
    Animation.prototype.getFrame = function() {
        this.loop();
        return this.conf.frames[this.cur_frame];
    };

    /**
     * Public stuff
     */
    return {

        create: function(conf) {
            return new Sprite(conf);
        },
    }
})();<|endoftext|>


<commit_before>/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the new BSD license.
 *
 * @author      David Zeller <me@zellerda.com>
 * @license     http://www.opensource.org/licenses/BSD-3-Clause New BSD license
 * @version     2.3
 */
(function($, tokenize){

    // Keycodes
    var KEYS = {
        BACKSPACE: 8,
        TAB: 9,
        ENTER: 13,
        ESCAPE: 27,
        ARROW_UP: 38,
        ARROW_DOWN: 40,
        COMMA: 188
    };

    // Data storage constant
    var DATA = 'tokenize';

    $.tokenize = function(opts){

        if(opts == undefined){
            opts = $.fn.tokenize.defaults;
        }

        this.options = opts;
    };

    $.extend($.tokenize.prototype, {

        init: function(el){

            var $this = this;
            this.select = el.attr('multiple', 'multiple').css({margin: 0, padding: 0, border: 0}).hide();

            this.container = $('<div />')
                .attr('class', this.select.attr('class'))
                .addClass('Tokenize');

            if(this.options.maxElements == 1){
                this.container.addClass('OnlyOne');
            }

            this.dropdown = $('<ul />')
                .addClass('Dropdown');

            this.tokensContainer = $('<ul />')
                .addClass('TokensContainer');

            this.searchToken = $('<li />')
                .addClass('TokenSearch')
                .appendTo(this.tokensContainer);

            this.searchInput = $('<input />')
                .appendTo(this.searchToken);

            if(this.options.searchMaxLength > 0){
                this.searchInput.attr('maxlength', this.options.searchMaxLength)
            }

            if(this.select.prop('disabled')){
                this.disable();
            }

            this.container
                .append(this.tokensContainer)
                .append(this.dropdown)
                .insertAfter(this.select);

            this.tokensContainer.on('click', function(e){
                e.stopImmediatePropagation();
                $this.searchInput.get(0).focus();
                $this.updatePlaceholder();
                if($this.dropdown.is(':hidden') && $this.searchInput.val() != ''){
                    $this.search();
                }
            });

            this.searchInput.on('focus click', function(){
                if($this.options.displayDropdownOnFocus && $this.options.datas == 'select'){
                    $this.search();
                }
            });

            this.searchInput.on('keydown', function(e){
                $this.resizeSearchInput();
                $this.keydown(e);
            });

            this.searchInput.on('keyup', function(e){
                $this.keyup(e);
            });

            this.searchInput.on('paste', function(){
                setTimeout(function(){ $this.resizeSearchInput(); }, 10);
                setTimeout(function(){
                    var paste_elements = $this.searchInput.val().split(',');
                    if(paste_elements.length > 1){
                        $.each(paste_elements, function(_, value){
                            $this.tokenAdd(value.trim(), '');
                        });
                    }
                }, 20);
            });

            $(document).on('click', function(){
                $this.dropdownHide();
                if($this.options.maxElements == 1){
                    if($this.searchInput.val()){
                        $this.tokenAdd($this.searchInput.val(), '');
                    }
                }
            });

            this.resizeSearchInput();

            $('option:selected', this.select).each(function(){
                $this.tokenAdd($(this).attr('value'), $(this).html(), true);
            });

            this.updatePlaceholder();

        },

        updatePlaceholder: function(){

            if(this.options.placeholder != false){
                if(this.placeholder == undefined){
                    this.placeholder = $('<li />').addClass('Placeholder').html(this.options.placeholder);
                    this.placeholder.insertBefore($('li:first-child', this.tokensContainer));
                }

                if(this.searchInput.val().length == 0 && $('li.Token', this.tokensContainer).length == 0){
                    this.placeholder.show();
                } else {
                    this.placeholder.hide();
                }
            }

        },

        dropdownShow: function(){

            this.dropdown.show();

        },

        dropdownPrev: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:first-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').prev().addClass('Hover');
                } else {
                    $('li.Hover', this.dropdown).removeClass('Hover');
                    $('li:last-child', this.dropdown).addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownNext: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:last-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').next().addClass('Hover');
                } else {
                    $('li.Hover', this.dropdown).removeClass('Hover');
                    $('li:first-child', this.dropdown).addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownAddItem: function(value, text, html){

            if(html == undefined){
                html = text;
            }

            if($('li[data-value="' + value + '"]', this.tokensContainer).length){
                return false;
            }

            var $this = this;
            var item = $('<li />')
                .attr('data-value', value)
                .attr('data-text', text)
                .html(html)
                .on('click', function(e){
                    e.stopImmediatePropagation();
                    $this.tokenAdd($(this).attr('data-value'), $(this).attr('data-text'));
                }).on('mouseover', function(){
                    $(this).addClass('Hover');
                }).on('mouseout', function(){
                    $('li', $this.dropdown).removeClass('Hover');
                });

            this.dropdown.append(item);
            return true;

        },

        dropdownHide: function(){

            this.dropdownReset();
            this.dropdown.hide();

        },

        dropdownReset: function(){

            this.dropdown.html('');

        },

        resizeSearchInput: function(){

            this.searchInput.attr('size', (this.searchInput.val().length > 1 ? this.searchInput.val().length : 5));
            this.updatePlaceholder();

        },

        resetSearchInput: function(){

            this.searchInput.val("");
            this.resizeSearchInput();

        },

        resetPendingTokens: function(){

            $('li.PendingDelete', this.tokensContainer).removeClass('PendingDelete');

        },

        keydown: function(e){

            if(e.keyCode == KEYS.COMMA){
                e.preventDefault();
                this.tokenAdd(this.searchInput.val(), '');
            } else {
                switch(e.keyCode){
                    case KEYS.BACKSPACE:
                        if(this.searchInput.val().length == 0){
                            e.preventDefault();
                            if($('li.Token.PendingDelete', this.tokensContainer).length){
                                this.tokenRemove($('li.Token.PendingDelete').attr('data-value'));
                            } else {
                                $('li.Token:last', this.tokensContainer).addClass('PendingDelete');
                            }
                            this.dropdownHide();
                        }
                        break;

                    case KEYS.TAB:
                    case KEYS.ENTER:
                        if($('li.Hover', this.dropdown).length){
                            var element = $('li.Hover', this.dropdown);
                            e.preventDefault();
                            this.tokenAdd(element.attr('data-value'), element.attr('data-text'));
                        } else {
                            if(this.searchInput.val()){
                                e.preventDefault();
                                this.tokenAdd(this.searchInput.val(), '');
                            }
                        }
                        this.resetPendingTokens();
                        break;

                    case KEYS.ESCAPE:
                        this.resetSearchInput();
                        this.dropdownHide();
                        this.resetPendingTokens();
                        break;

                    case KEYS.ARROW_UP:
                        e.preventDefault();
                        this.dropdownPrev();
                        break;

                    case KEYS.ARROW_DOWN:
                        e.preventDefault();
                        this.dropdownNext();
                        break;

                    default:
                        this.resetPendingTokens();
                        break;
                }
            }

        },

        keyup: function(e){

            this.updatePlaceholder();
            if(e.keyCode != this.options.validator){
                switch(e.keyCode){
                    case KEYS.TAB:
                    case KEYS.ENTER:
                    case KEYS.ESCAPE:
                    case KEYS.ARROW_UP:
                    case KEYS.ARROW_DOWN:
                        break;

                    case KEYS.BACKSPACE:
                        if(this.searchInput.val()){
                            this.search();
                        } else {
                            this.dropdownHide();
                        }
                        break;
                    default:
                        if(this.searchInput.val()){
                            this.search();
                        }
                        break;
                }
            }

        },

        search: function(){

            var $this = this;
            var count = 1;

            if(this.options.maxElements > 0 && $('li.Token', this.tokensContainer).length >= this.options.maxElements){
                return false;
            }

            if(this.options.datas == 'select'){

                var found = false, regexp = new RegExp(this.searchInput.val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
                this.dropdownReset();

                $('option', this.select).not(':selected').each(function(){
                    if(count <= $this.options.nbDropdownElements){
                        if(regexp.test($(this).html())){
                            $this.dropdownAddItem($(this).attr('value'), $(this).html());
                            found = true;
                            count++;
                        }
                    } else {
                        return false;
                    }
                });

                if(found){
                    $('li:first', this.dropdown).addClass('Hover');
                    this.dropdownShow();
                } else {
                    this.dropdownHide();
                }

            } else {

                $.ajax({
                    url: this.options.datas,
                    data: this.options.searchParam + "=" + this.searchInput.val(),
                    dataType: this.options.dataType,
                    success: function(data){
                        if(data){
                            $this.dropdownReset();
                            $.each(data, function(key, val){
                                if(count <= $this.options.nbDropdownElements){
                                    var html = undefined;
                                    if(val[$this.options.htmlField]){
                                        html = val[$this.options.htmlField];
                                    }
                                    $this.dropdownAddItem(val[$this.options.valueField], val[$this.options.textField], html);
                                    count++;
                                } else {
                                    return false;
                                }
                            });
                            if($('li', $this.dropdown).length){
                                $('li:first', $this.dropdown).addClass('Hover');
                                $this.dropdownShow();
                                return true;
                            }
                        }
                        $this.dropdownHide();
                    },
                    error: function(XHR, textStatus) {
                        console.log("Error : " + textStatus);
                    }
                });

            }

        },

        tokenAdd: function(value, text, first){

            if(value == undefined || value == ''){
                return false;
            }

            if(text == undefined || text == ''){
                text = value;
            }

            if(first == undefined){
                first = false;
            }

            if(this.options.maxElements > 0 && $('li.Token', this.tokensContainer).length >= this.options.maxElements){
                this.resetSearchInput();
                return false;
            }

            var $this = this;
            var close_btn = $('<a />')
                .addClass('Close')
                .html("×")
                .on('click', function(e){
                    e.stopImmediatePropagation();
                    $this.tokenRemove(value);
                });

            if($('option[value="' + value + '"]', this.select).length){
                $('option[value="' + value + '"]', this.select).attr('selected', 'selected');
            } else if(this.options.newElements || (!this.options.newElements && $('li[data-value="' + value + '"]', this.dropdown).length > 0)) {
                var option = $('<option />')
                    .attr('selected', 'selected')
                    .attr('value', value)
                    .attr('data-type', 'custom')
                    .html(text);

                this.select.append(option);
            } else {
                this.resetSearchInput();
                return false;
            }

            if($('li.Token[data-value="' + value + '"]', this.tokensContainer).length > 0) {
                return false;
            }

            $('<li />')
                .addClass('Token')
                .attr('data-value', value)
                .append('<span>' + text + '</span>')
                .prepend(close_btn)
                .insertBefore(this.searchToken);

            if(!first){
                this.options.onAddToken(value, text);
            }

            this.resetSearchInput();
            this.dropdownHide();

            return true;

        },

        tokenRemove: function(value){

            var option = $('option[value="' + value + '"]', this.select);

            if(option.attr('data-type') == 'custom'){
                option.remove();
            } else {
                option.removeAttr('selected');
            }

            $('li.Token[data-value="' + value + '"]', this.tokensContainer).remove();

            this.options.onRemoveToken(value);
            this.resizeSearchInput();
            this.dropdownHide();

        },

        clear: function(){

            var $this = this;
            $('li.Token', this.tokensContainer).each(function(){
                $this.tokenRemove($(this).attr('data-value'));
            });

            this.options.onClear();

        },

        disable: function(){
            this.select.prop('disabled', true);
            this.searchInput.prop('disabled', true);
            this.container.addClass('Disabled');
        },

        enable: function(){
            this.select.prop('disabled', false);
            this.searchInput.prop('disabled', false);
            this.container.removeClass('Disabled');
        }

    });

    $.fn.tokenize = function(options){

        if(options == undefined){
            options = {};
        }

        this.each(function(){
            var obj = new $.tokenize($.extend({}, $.fn.tokenize.defaults, options));
            obj.init($(this));
            $(this).data(DATA, obj);
        });

        return this;

    };

    $.fn.tokenize.defaults = {

        datas: 'select',
        placeholder: false,
        searchParam: 'search',
        searchMaxLength: 0,
        newElements: true,
        nbDropdownElements: 10,
        displayDropdownOnFocus: false,
        maxElements: 0,
        dataType: 'json',
        valueField: 'value',
        textField: 'text',
        htmlField: 'html',

        onAddToken: function(value, text){},
        onRemoveToken: function(value){},
        onClear: function(){}

    };

})(jQuery, 'tokenize');
<commit_msg>Add current tokenize object for all callback function with e parameter<commit_after>/**
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the new BSD license.
 *
 * @author      David Zeller <me@zellerda.com>
 * @license     http://www.opensource.org/licenses/BSD-3-Clause New BSD license
 * @version     2.3
 */
(function($, tokenize){

    // Keycodes
    var KEYS = {
        BACKSPACE: 8,
        TAB: 9,
        ENTER: 13,
        ESCAPE: 27,
        ARROW_UP: 38,
        ARROW_DOWN: 40,
        COMMA: 188
    };

    // Data storage constant
    var DATA = 'tokenize';

    $.tokenize = function(opts){

        if(opts == undefined){
            opts = $.fn.tokenize.defaults;
        }

        this.options = opts;
    };

    $.extend($.tokenize.prototype, {

        init: function(el){

            var $this = this;
            this.select = el.attr('multiple', 'multiple').css({margin: 0, padding: 0, border: 0}).hide();

            this.container = $('<div />')
                .attr('class', this.select.attr('class'))
                .addClass('Tokenize');

            if(this.options.maxElements == 1){
                this.container.addClass('OnlyOne');
            }

            this.dropdown = $('<ul />')
                .addClass('Dropdown');

            this.tokensContainer = $('<ul />')
                .addClass('TokensContainer');

            this.searchToken = $('<li />')
                .addClass('TokenSearch')
                .appendTo(this.tokensContainer);

            this.searchInput = $('<input />')
                .appendTo(this.searchToken);

            if(this.options.searchMaxLength > 0){
                this.searchInput.attr('maxlength', this.options.searchMaxLength)
            }

            if(this.select.prop('disabled')){
                this.disable();
            }

            this.container
                .append(this.tokensContainer)
                .append(this.dropdown)
                .insertAfter(this.select);

            this.tokensContainer.on('click', function(e){
                e.stopImmediatePropagation();
                $this.searchInput.get(0).focus();
                $this.updatePlaceholder();
                if($this.dropdown.is(':hidden') && $this.searchInput.val() != ''){
                    $this.search();
                }
            });

            this.searchInput.on('focus click', function(){
                if($this.options.displayDropdownOnFocus && $this.options.datas == 'select'){
                    $this.search();
                }
            });

            this.searchInput.on('keydown', function(e){
                $this.resizeSearchInput();
                $this.keydown(e);
            });

            this.searchInput.on('keyup', function(e){
                $this.keyup(e);
            });

            this.searchInput.on('paste', function(){
                setTimeout(function(){ $this.resizeSearchInput(); }, 10);
                setTimeout(function(){
                    var paste_elements = $this.searchInput.val().split(',');
                    if(paste_elements.length > 1){
                        $.each(paste_elements, function(_, value){
                            $this.tokenAdd(value.trim(), '');
                        });
                    }
                }, 20);
            });

            $(document).on('click', function(){
                $this.dropdownHide();
                if($this.options.maxElements == 1){
                    if($this.searchInput.val()){
                        $this.tokenAdd($this.searchInput.val(), '');
                    }
                }
            });

            this.resizeSearchInput();

            $('option:selected', this.select).each(function(){
                $this.tokenAdd($(this).attr('value'), $(this).html(), true);
            });

            this.updatePlaceholder();

        },

        updatePlaceholder: function(){

            if(this.options.placeholder != false){
                if(this.placeholder == undefined){
                    this.placeholder = $('<li />').addClass('Placeholder').html(this.options.placeholder);
                    this.placeholder.insertBefore($('li:first-child', this.tokensContainer));
                }

                if(this.searchInput.val().length == 0 && $('li.Token', this.tokensContainer).length == 0){
                    this.placeholder.show();
                } else {
                    this.placeholder.hide();
                }
            }

        },

        dropdownShow: function(){

            this.dropdown.show();

        },

        dropdownPrev: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:first-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').prev().addClass('Hover');
                } else {
                    $('li.Hover', this.dropdown).removeClass('Hover');
                    $('li:last-child', this.dropdown).addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownNext: function(){

            if($('li.Hover', this.dropdown).length > 0){
                if(!$('li.Hover', this.dropdown).is('li:last-child')){
                    $('li.Hover', this.dropdown).removeClass('Hover').next().addClass('Hover');
                } else {
                    $('li.Hover', this.dropdown).removeClass('Hover');
                    $('li:first-child', this.dropdown).addClass('Hover');
                }
            } else {
                $('li:first', this.dropdown).addClass('Hover');
            }

        },

        dropdownAddItem: function(value, text, html){

            if(html == undefined){
                html = text;
            }

            if($('li[data-value="' + value + '"]', this.tokensContainer).length){
                return false;
            }

            var $this = this;
            var item = $('<li />')
                .attr('data-value', value)
                .attr('data-text', text)
                .html(html)
                .on('click', function(e){
                    e.stopImmediatePropagation();
                    $this.tokenAdd($(this).attr('data-value'), $(this).attr('data-text'));
                }).on('mouseover', function(){
                    $(this).addClass('Hover');
                }).on('mouseout', function(){
                    $('li', $this.dropdown).removeClass('Hover');
                });

            this.dropdown.append(item);
            return true;

        },

        dropdownHide: function(){

            this.dropdownReset();
            this.dropdown.hide();

        },

        dropdownReset: function(){

            this.dropdown.html('');

        },

        resizeSearchInput: function(){

            this.searchInput.attr('size', (this.searchInput.val().length > 1 ? this.searchInput.val().length : 5));
            this.updatePlaceholder();

        },

        resetSearchInput: function(){

            this.searchInput.val("");
            this.resizeSearchInput();

        },

        resetPendingTokens: function(){

            $('li.PendingDelete', this.tokensContainer).removeClass('PendingDelete');

        },

        keydown: function(e){

            if(e.keyCode == KEYS.COMMA){
                e.preventDefault();
                this.tokenAdd(this.searchInput.val(), '');
            } else {
                switch(e.keyCode){
                    case KEYS.BACKSPACE:
                        if(this.searchInput.val().length == 0){
                            e.preventDefault();
                            if($('li.Token.PendingDelete', this.tokensContainer).length){
                                this.tokenRemove($('li.Token.PendingDelete').attr('data-value'));
                            } else {
                                $('li.Token:last', this.tokensContainer).addClass('PendingDelete');
                            }
                            this.dropdownHide();
                        }
                        break;

                    case KEYS.TAB:
                    case KEYS.ENTER:
                        if($('li.Hover', this.dropdown).length){
                            var element = $('li.Hover', this.dropdown);
                            e.preventDefault();
                            this.tokenAdd(element.attr('data-value'), element.attr('data-text'));
                        } else {
                            if(this.searchInput.val()){
                                e.preventDefault();
                                this.tokenAdd(this.searchInput.val(), '');
                            }
                        }
                        this.resetPendingTokens();
                        break;

                    case KEYS.ESCAPE:
                        this.resetSearchInput();
                        this.dropdownHide();
                        this.resetPendingTokens();
                        break;

                    case KEYS.ARROW_UP:
                        e.preventDefault();
                        this.dropdownPrev();
                        break;

                    case KEYS.ARROW_DOWN:
                        e.preventDefault();
                        this.dropdownNext();
                        break;

                    default:
                        this.resetPendingTokens();
                        break;
                }
            }

        },

        keyup: function(e){

            this.updatePlaceholder();
            if(e.keyCode != this.options.validator){
                switch(e.keyCode){
                    case KEYS.TAB:
                    case KEYS.ENTER:
                    case KEYS.ESCAPE:
                    case KEYS.ARROW_UP:
                    case KEYS.ARROW_DOWN:
                        break;

                    case KEYS.BACKSPACE:
                        if(this.searchInput.val()){
                            this.search();
                        } else {
                            this.dropdownHide();
                        }
                        break;
                    default:
                        if(this.searchInput.val()){
                            this.search();
                        }
                        break;
                }
            }

        },

        search: function(){

            var $this = this;
            var count = 1;

            if(this.options.maxElements > 0 && $('li.Token', this.tokensContainer).length >= this.options.maxElements){
                return false;
            }

            if(this.options.datas == 'select'){

                var found = false, regexp = new RegExp(this.searchInput.val().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
                this.dropdownReset();

                $('option', this.select).not(':selected').each(function(){
                    if(count <= $this.options.nbDropdownElements){
                        if(regexp.test($(this).html())){
                            $this.dropdownAddItem($(this).attr('value'), $(this).html());
                            found = true;
                            count++;
                        }
                    } else {
                        return false;
                    }
                });

                if(found){
                    $('li:first', this.dropdown).addClass('Hover');
                    this.dropdownShow();
                } else {
                    this.dropdownHide();
                }

            } else {

                $.ajax({
                    url: this.options.datas,
                    data: this.options.searchParam + "=" + this.searchInput.val(),
                    dataType: this.options.dataType,
                    success: function(data){
                        if(data){
                            $this.dropdownReset();
                            $.each(data, function(key, val){
                                if(count <= $this.options.nbDropdownElements){
                                    var html = undefined;
                                    if(val[$this.options.htmlField]){
                                        html = val[$this.options.htmlField];
                                    }
                                    $this.dropdownAddItem(val[$this.options.valueField], val[$this.options.textField], html);
                                    count++;
                                } else {
                                    return false;
                                }
                            });
                            if($('li', $this.dropdown).length){
                                $('li:first', $this.dropdown).addClass('Hover');
                                $this.dropdownShow();
                                return true;
                            }
                        }
                        $this.dropdownHide();
                    },
                    error: function(XHR, textStatus) {
                        console.log("Error : " + textStatus);
                    }
                });

            }

        },

        tokenAdd: function(value, text, first){

            if(value == undefined || value == ''){
                return false;
            }

            if(text == undefined || text == ''){
                text = value;
            }

            if(first == undefined){
                first = false;
            }

            if(this.options.maxElements > 0 && $('li.Token', this.tokensContainer).length >= this.options.maxElements){
                this.resetSearchInput();
                return false;
            }

            var $this = this;
            var close_btn = $('<a />')
                .addClass('Close')
                .html("×")
                .on('click', function(e){
                    e.stopImmediatePropagation();
                    $this.tokenRemove(value);
                });

            if($('option[value="' + value + '"]', this.select).length){
                $('option[value="' + value + '"]', this.select).attr('selected', 'selected');
            } else if(this.options.newElements || (!this.options.newElements && $('li[data-value="' + value + '"]', this.dropdown).length > 0)) {
                var option = $('<option />')
                    .attr('selected', 'selected')
                    .attr('value', value)
                    .attr('data-type', 'custom')
                    .html(text);

                this.select.append(option);
            } else {
                this.resetSearchInput();
                return false;
            }

            if($('li.Token[data-value="' + value + '"]', this.tokensContainer).length > 0) {
                return false;
            }

            $('<li />')
                .addClass('Token')
                .attr('data-value', value)
                .append('<span>' + text + '</span>')
                .prepend(close_btn)
                .insertBefore(this.searchToken);

            if(!first){
                this.options.onAddToken(this, value, text);
            }

            this.resetSearchInput();
            this.dropdownHide();

            return true;

        },

        tokenRemove: function(value){

            var option = $('option[value="' + value + '"]', this.select);

            if(option.attr('data-type') == 'custom'){
                option.remove();
            } else {
                option.removeAttr('selected');
            }

            $('li.Token[data-value="' + value + '"]', this.tokensContainer).remove();

            this.options.onRemoveToken(this, value);
            this.resizeSearchInput();
            this.dropdownHide();

        },

        clear: function(){

            var $this = this;
            $('li.Token', this.tokensContainer).each(function(){
                $this.tokenRemove($(this), $(this).attr('data-value'));
            });

            this.options.onClear(this);

        },

        disable: function(){
            this.select.prop('disabled', true);
            this.searchInput.prop('disabled', true);
            this.container.addClass('Disabled');
        },

        enable: function(){
            this.select.prop('disabled', false);
            this.searchInput.prop('disabled', false);
            this.container.removeClass('Disabled');
        }

    });

    $.fn.tokenize = function(options){

        if(options == undefined){
            options = {};
        }

        this.each(function(){
            var obj = new $.tokenize($.extend({}, $.fn.tokenize.defaults, options));
            obj.init($(this));
            $(this).data(DATA, obj);
        });

        return this;

    };

    $.fn.tokenize.defaults = {

        datas: 'select',
        placeholder: false,
        searchParam: 'search',
        searchMaxLength: 0,
        newElements: true,
        nbDropdownElements: 10,
        displayDropdownOnFocus: false,
        maxElements: 0,
        dataType: 'json',
        valueField: 'value',
        textField: 'text',
        htmlField: 'html',

        onAddToken: function(e, value, text){},
        onRemoveToken: function(e, value){},
        onClear: function(e){}

    };

})(jQuery, 'tokenize');
<|endoftext|>


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

    angular.module("mPlatform.services")
        .factory("guidGenerator", function () {
            var generatePart = function () {
                var guidPartNumber = (Math.random() * 0x10000) | 0;
                return (guidPartNumber + 0x10000).toString(16).substring(1).toUpperCase();
            };

            return function () {
                return generatePart()
                    + generatePart()
                    + "-"
                    + generatePart()
                    + "-"
                    + generatePart()
                    + "-"
                    + generatePart()
                    + "-"
                    + generatePart()
                    + generatePart()
                    + generatePart();
            };
        })

        .factory("products", ["$rootScope", "$q", "$window", "$resource", function ($rootScope, $q, $window, $resource) {
            var key = ''; // Add your Bing Maps API key
            var url = 'http://labyrinthapis.kusanii.net/api/product/';

            return {
                getSearchResults: function (searchText) {
                    return $resource(url, {})
                        .query({ text: searchText })
                        .$promise.then(function (response) {
                            return response;
                        }, function (error) {
                            throw error;
                        //return "Error" + error;
                    });
                }
            }
        }])
        .factory("cordova", ['$q', "$window", "$timeout", function ($q, $window, $timeout) {
            var deferred = $q.defer();
            var resolved = false;

            document.addEventListener('deviceready', function () {
                resolved = true;
                deferred.resolve($window.cordova);
            }, false);

            $timeout(function () {
                if (!resolved && $window.cordova) {
                    deferred.resolve($window.cordova);
                }
            });

            return { ready: deferred.promise };
        }]);
})();<commit_msg>fixed url<commit_after>(function () {
    'use strict';

    angular.module("mPlatform.services")
        .factory("guidGenerator", function () {
            var generatePart = function () {
                var guidPartNumber = (Math.random() * 0x10000) | 0;
                return (guidPartNumber + 0x10000).toString(16).substring(1).toUpperCase();
            };

            return function () {
                return generatePart()
                    + generatePart()
                    + "-"
                    + generatePart()
                    + "-"
                    + generatePart()
                    + "-"
                    + generatePart()
                    + "-"
                    + generatePart()
                    + generatePart()
                    + generatePart();
            };
        })

        .factory("products", ["$rootScope", "$q", "$window", "$resource", function ($rootScope, $q, $window, $resource) {
            var key = ''; // Add your Bing Maps API key
            var url = 'http://labyrinthapi.kusanii.net/api/product/';

            return {
                getSearchResults: function (searchText) {
                    return $resource(url, {})
                        .query({ text: searchText })
                        .$promise.then(function (response) {
                            return response;
                        }, function (error) {
                            throw error;
                        //return "Error" + error;
                    });
                }
            }
        }])
        .factory("cordova", ['$q', "$window", "$timeout", function ($q, $window, $timeout) {
            var deferred = $q.defer();
            var resolved = false;

            document.addEventListener('deviceready', function () {
                resolved = true;
                deferred.resolve($window.cordova);
            }, false);

            $timeout(function () {
                if (!resolved && $window.cordova) {
                    deferred.resolve($window.cordova);
                }
            });

            return { ready: deferred.promise };
        }]);
})();<|endoftext|>


<commit_before>function native_click(el)
{
    var els_anchor = el.children('a')[0];
    var clickEvent = document.createEvent("HTMLEvents");
    clickEvent.initEvent("click", true, true);
    els_anchor.dispatchEvent(clickEvent);
}

$(document).keydown(function(event) {
    if (event.altKey === true && event.shiftKey === true) {
        if (event.key == 'ArrowUp') {
            native_click($('.hc-tab.aui-nav-selected').prevAll('.hc-has-badge').first());
        }
        else if (event.key == 'ArrowDown') {
            native_click($('.hc-tab.aui-nav-selected').nextAll('.hc-has-badge').first());
        }
    }
    else if (event.altKey === true && event.shiftKey === false)
    {
        var selected = $('.hc-tab.aui-nav-selected')[0];
        var target_list = $('.hc-room').toArray().concat($('.hc-person').toArray());
        var current_index = target_list.indexOf(selected);

        if (event.key == 'ArrowUp') {
            new_index = (current_index - 1 + target_list.length) % target_list.length
            var next = $(target_list[new_index]);
            native_click(next);
        }
        else if (event.key == 'ArrowDown') {
            new_index = (current_index + 1) % target_list.length
            var next = $(target_list[new_index]);
            native_click(next);
        }
    }
})

<commit_msg>Fix bug related to finding neighbour, dont use prevAll, nextAll.<commit_after>function native_click(el)
{
    var els_anchor = el.children('a')[0];
    var clickEvent = document.createEvent("HTMLEvents");
    clickEvent.initEvent("click", true, true);
    els_anchor.dispatchEvent(clickEvent);
}

function step(current, list, step) {
    var current_index = list.index(current);
    var new_index = (current_index + step + list.length) % list.length
    return $(list[new_index]);
}

$(document).keydown(function(event) {
    if (event.altKey === true && event.shiftKey === true &&
            (event.key == 'ArrowUp' || event.key == 'ArrowDown'))
    {
        var target_list = $('.hc-tab.aui-nav-selected, .hc-has-badge');
        if (target_list.length > 1) {
            if (event.key == 'ArrowUp') {
                var next = step($('.hc-tab.aui-nav-selected')[0], target_list, -1)
            }
            else if (event.key == 'ArrowDown') {
                var next = step($('.hc-tab.aui-nav-selected')[0], target_list, 1)
            }
            native_click(next);
        }
    }
    else if (event.altKey === true && event.shiftKey === false &&
            (event.key == 'ArrowUp' || event.key == 'ArrowDown'))
    {
        var target_list = $('.hc-room, .hc-person');

        if (event.key == 'ArrowUp') {
            var next = step($('.hc-tab.aui-nav-selected')[0], target_list, -1)
        }
        else if (event.key == 'ArrowDown') {
            var next = step($('.hc-tab.aui-nav-selected')[0], target_list, 1)
        }
        native_click(next);
    }
})
<|endoftext|>


<commit_before>chai.should();

describe('Works when the value is as html', function() {
	it("should work on paragraph elements", function() {
		var p = $('
').text('john.doe@company.com');
		p.is('email').should.be.true;
	});

	it('should work on input elements', function() {
		var input = $('<input>').val('john.doe@company.com');
		input.is('email').should.be.true;
	});

	it("should work on strings", function() {
		$.is('john.doe@company.com', 'email').should.be.true;
	});
});

describe("Regular expression keywords", function() {

	describe("Credit cards", function() {
		it("Works for credit cards", function() {
			$.is('1234123412341234', 'cc').should.be.true;
		});
	});

	describe("Date and time", function() {
		it("Format: 1996-12-19T16:39:57-08:00", function() {
			$.is('1996-12-19T16:39:57-08:00', 'datetime').should.be.true;
		});
	});

	describe("ISBN", function() {
		it("ISBN 10", function() {
			$.is('0-85131-041-9', 'isbn').should.be.true;
			$.is('0851310419', 'isbn').should.be.true;
		});

		it("ISBN 13", function() {
			$.is('978-1-56619-909-4', 'isbn').should.be.true;
			$.is('9781566199094', 'isbn').should.be.true;
		});
	});

	describe("Latitude and longitude", function() {
		it("Latitude", function() {
			$.is('64.163296', 'latlng');
		});

		it("Longitude", function() {
			$.is('-21.859328', 'latlng');
		});
	});

	describe("Phone numbers", function() {
		it("Iceland - format: +354 ###-####", function() {
			$.is('1212525', 'phone:is').should.be.true;
			$.is('121 2525', 'phone:is').should.be.true;
			$.is('121 -2525', 'phone:is').should.be.true;
			$.is('121- 2525', 'phone:is').should.be.true;
			$.is('121 - 2525', 'phone:is').should.be.true;
			$.is('121-2525', 'phone:is').should.be.true;
		});
	});

	describe("Zip codes for countries", function() {
		it("Australia - format: ####", function() {
			$.is('1122', 'zip:au').should.be.true;
		});

		it("Austria - format: ####", function() {
			$.is('1122', 'zip:at').should.be.true;
		});

		it("Belgium - format: ####", function() {
			$.is('1122', 'zip:be').should.be.true;
		});

		it("Brazil - format: #####-###", function() {
			$.is('25251-121', 'zip:br').should.be.true;
		});

		it("Canada - format: A#A #A#", function() {
			$.is('K2A 7B7', 'zip:ca').should.be.true;
		});

		it("Danmark - format: ### (or) ####", function() {
			$.is('123', 'zip:dk').should.be.true;
			$.is('1234', 'zip:dk').should.be.true;
		});

		it("Germany - format: #####", function() {
			$.is('12345', 'zip:de').should.be.true;
		});

		it("Great Britain - format: AA#A #AA (or) A#A #AA (or) A# #AA (or) A## #AA (or) AA# #AA (or) AA## #AA", function() {
			$.is('gj5g 5hh', 'zip:gb').should.be.true;
			$.is('h7u 5hh', 'zip:gb').should.be.true;
			$.is('h7 5hh', 'zip:gb').should.be.true;
			$.is('h76 5hh', 'zip:gb').should.be.true;
			$.is('hf6 5hh', 'zip:gb').should.be.true;
			$.is('hf63 5hh', 'zip:gb').should.be.true;
		});

		it("Hungry - format: ####", function() {
			$.is('1234', 'zip:hu').should.be.true;
		});

		it("Iceland - format: ###", function() {
			$.is('112', 'zip:is').should.be.true;
		});

		it("Italy - format: #####", function() {
			$.is('12345', 'zip:it').should.be.true;
		});

		it("Japan - format: ###-####", function() {
			$.is('123-1234', 'zip:jp').should.be.true;
		});

		it("Luxembourg - format: L-####", function() {
			$.is('L-1234', 'zip:lu').should.be.true;
		});

		it("Nertherland - format: 1### AA", function() {
			$.is('1234 aG', 'zip:nl').should.be.true;
			$.is('1234AA', 'zip:nl').should.be.true;
		});

		it("Polland - format: ##-###", function() {
			$.is('12-123', 'zip:pl').should.be.true;
		});

		it("Spain - format: [01-50]###", function() {
			$.is('01123', 'zip:es').should.be.true;
			$.is('50123', 'zip:es').should.be.true;
		});

		it("Sweden - format: ### ##", function() {
			$.is('12312', 'zip:se').should.be.true;
			$.is('123 12', 'zip:se').should.be.true;
		});
		it("United States of America - format: ##### (or) #####-####", function() {
			$.is('12135', 'zip:us').should.be.true;
			$.is('12135-4342', 'zip:us').should.be.true;
		});
	});
});<commit_msg>Add us and fr tests<commit_after>chai.should();

describe('Works when the value is as html', function() {
	it("should work on paragraph elements", function() {
		var p = $('
').text('john.doe@company.com');
		p.is('email').should.be.true;
	});

	it('should work on input elements', function() {
		var input = $('<input>').val('john.doe@company.com');
		input.is('email').should.be.true;
	});

	it("should work on strings", function() {
		$.is('john.doe@company.com', 'email').should.be.true;
	});
});

describe("Regular expression keywords", function() {

	describe("Credit cards", function() {
		it("Works for credit cards", function() {
			$.is('1234123412341234', 'cc').should.be.true;
		});
	});

	describe("Date and time", function() {
		it("Format: 1996-12-19T16:39:57-08:00", function() {
			$.is('1996-12-19T16:39:57-08:00', 'datetime').should.be.true;
		});
	});

	describe("ISBN", function() {
		it("ISBN 10", function() {
			$.is('0-85131-041-9', 'isbn').should.be.true;
			$.is('0851310419', 'isbn').should.be.true;
		});

		it("ISBN 13", function() {
			$.is('978-1-56619-909-4', 'isbn').should.be.true;
			$.is('9781566199094', 'isbn').should.be.true;
		});
	});

	describe("Latitude and longitude", function() {
		it("Latitude", function() {
			$.is('64.163296', 'latlng');
		});

		it("Longitude", function() {
			$.is('-21.859328', 'latlng');
		});
	});

	describe("Phone numbers", function() {
		it("France", function() {
			$.is('0423 23 12 24', 'phone:fr');
			$.is('+33423 23 12 24', 'phone:fr');
			$.is('0033423 23 12 24', 'phone:fr');
		});

		it("Iceland - format: +354 ###-####", function() {
			$.is('123-4567', 'phone:is').should.be.true;
			$.is('+354 123-4567', 'phone:is').should.be.true;
		});

		it("United States of America - format: ###-###-####", function() {
			$.is('123-456-7890', 'phone:us');
		});
	});

	describe("Zip codes for countries", function() {
		it("Australia - format: ####", function() {
			$.is('1122', 'zip:au').should.be.true;
		});

		it("Austria - format: ####", function() {
			$.is('1122', 'zip:at').should.be.true;
		});

		it("Belgium - format: ####", function() {
			$.is('1122', 'zip:be').should.be.true;
		});

		it("Brazil - format: #####-###", function() {
			$.is('25251-121', 'zip:br').should.be.true;
		});

		it("Canada - format: A#A #A#", function() {
			$.is('K2A 7B7', 'zip:ca').should.be.true;
		});

		it("Danmark - format: ### (or) ####", function() {
			$.is('123', 'zip:dk').should.be.true;
			$.is('1234', 'zip:dk').should.be.true;
		});

		it("Germany - format: #####", function() {
			$.is('12345', 'zip:de').should.be.true;
		});

		it("Great Britain - format: AA#A #AA (or) A#A #AA (or) A# #AA (or) A## #AA (or) AA# #AA (or) AA## #AA", function() {
			$.is('gj5g 5hh', 'zip:gb').should.be.true;
			$.is('h7u 5hh', 'zip:gb').should.be.true;
			$.is('h7 5hh', 'zip:gb').should.be.true;
			$.is('h76 5hh', 'zip:gb').should.be.true;
			$.is('hf6 5hh', 'zip:gb').should.be.true;
			$.is('hf63 5hh', 'zip:gb').should.be.true;
		});

		it("Hungry - format: ####", function() {
			$.is('1234', 'zip:hu').should.be.true;
		});

		it("Iceland - format: ###", function() {
			$.is('112', 'zip:is').should.be.true;
		});

		it("Italy - format: #####", function() {
			$.is('12345', 'zip:it').should.be.true;
		});

		it("Japan - format: ###-####", function() {
			$.is('123-1234', 'zip:jp').should.be.true;
		});

		it("Luxembourg - format: L-####", function() {
			$.is('L-1234', 'zip:lu').should.be.true;
		});

		it("Nertherland - format: 1### AA", function() {
			$.is('1234 aG', 'zip:nl').should.be.true;
			$.is('1234AA', 'zip:nl').should.be.true;
		});

		it("Polland - format: ##-###", function() {
			$.is('12-123', 'zip:pl').should.be.true;
		});

		it("Spain - format: [01-50]###", function() {
			$.is('01123', 'zip:es').should.be.true;
			$.is('50123', 'zip:es').should.be.true;
		});

		it("Sweden - format: ### ##", function() {
			$.is('12312', 'zip:se').should.be.true;
			$.is('123 12', 'zip:se').should.be.true;
		});
		it("United States of America - format: ##### (or) #####-####", function() {
			$.is('12135', 'zip:us').should.be.true;
			$.is('12135-4342', 'zip:us').should.be.true;
		});
	});
});<|endoftext|>