<commit_before>define(function(require, exports, module) {
    main.consumes = [
        "c9", "Editor", "editors", "util", "settings", "Menu", "ui",
        "preferences", "layout", "tabManager", "tree"
    ];
    main.provides = ["preview"];
    return main;

    // @todo - Add XML Plugin
    // @todo - Add JSON Plugin
    // @todo - Add Coffee Plugin
    // @todo - Add Jade Plugin
    // @todo - Add HTML/CSS/JS (auto-updating) Plugin
    // @todo - Add additional functionality, such as popout (levels, in editor, pane, browser pane)
    // @todo - Fix the activate/deactivate events on session. They leak / are not cleaned up

    function main(options, imports, register) {
        var Editor   = imports.Editor;
        var editors  = imports.editors;
        var c9       = imports.c9;
        var ui       = imports.ui;
        var util     = imports.util;
        var settings = imports.settings;
        var layout   = imports.layout;
        var tree     = imports.tree;
        var tabs     = imports.tabManager;
        var prefs    = imports.preferences;
        var Menu     = imports.Menu;

        var extensions = [];
        var counter    = 0;

        /***** Initialization *****/

        var handle     = editors.register("preview", "Preview",
                                           Preview, extensions);
        var handleEmit = handle.getEmitter();

        var previewers = {};
        var menu;

        function load(){
            var parent = layout.findParent({ name: "preview" });
            var button = !options.hideButton && new ui.button({
                skin     : "c9-toolbarbutton-glossy",
                "class"  : "preview",
                tooltip  : "Preview the current document",
                caption  : "Preview",
                disabled : true,
                onclick  : function() {
                    var tab = tabs.focussedTab;
                    if (tab && tab.editor.type === "preview" || !tab.path)
                        return;

                    // Find a good location to open preview side-by-side
                    var pane;
                    var otherPreview = search();
                    if (otherPreview && tab.pane != otherPreview) {
                        pane = otherPreview;
                    }
                    else {
                        var nodes = tab.pane.group;
                        if (!nodes)
                            pane = tab.pane.hsplit(true);
                        else {
                            pane = nodes[nodes.indexOf(tab.pane) === 0 ? 1 : 0];
                        }
                    }

                    // Open Preview
                    openPreview(tab.path, pane);
                }
            });
            button && ui.insertByIndex(parent, button, 10, handle);

            tabs.on("focus", function(e){
                var disabled = typeof e.tab.path != "string";
                button && button.setAttribute("disabled", disabled);
            }, handle);

            tabs.on("tabDestroy", function(e){
                if (e.last && button)
                    button.disable();
            }, handle);

            settings.on("read", function(e){
                settings.setDefaults("user/preview", [
                    ["running_app", "false"],
                    ["default", options.defaultPreviewer || "raw"]
                ]);
            }, handle);

            // Preferences
            prefs.add({
                "Run" : {
                    position : 600,
                    "Preview" : {
                        position : 200,
                        "Preview Running Apps" : {
                            type : "checkbox",
                            path : "user/preview/@running_app",
                            position : 4000
                        },
                        "Default Previewer" : {
                            type : "dropdown",
                            path : "user/preview/@default",
                            position : 5000,
                            items : [
                                // @todo this should come from plugin api
                                { caption: "Raw", value: "preview.raw" },
                                { caption: "Browser", value: "preview.browser" }
                            ]
                        },
                    }
                }
            }, handle);

            // Context menu for tree
            var itemCtxTreePreview = new apf.item({
                caption : "Preview",
                isAvailable : function(){
                    return tree.selectedNode && !tree.selectedNode.isFolder;
                },
                onclick : function(){
                    openPreview(tree.selected);
                }
            });
            tree.getElement("mnuCtxTree", function(mnuCtxTree) {
                ui.insertByIndex(mnuCtxTree, itemCtxTreePreview, 160, handle);
            });

            menu = new Menu({}, handle);
        }

        var drawn = false;
        function drawHandle(){
            if (drawn) return;
            drawn = true;

            // Import Skin
            ui.insertSkin({
                name         : "previewskin",
                data         : require("text!./skin.xml"),
                "media-path" : options.staticPrefix + "/images/",
                "icon-path"  : options.staticPrefix + "/images/"
            }, handle);

            // Import CSS
            var css = util.replaceStaticPrefix(require("text!./style.css"));
            ui.insertCss(css, handle);

            handleEmit("draw", null, true);
        }

        //Search through pages
        function search(){
            var pane;
            tabs.getTabs().every(function(tab){
                if (tab.editorType == "preview") {
                    pane = tab.pane;
                    return false;
                }
                return true;
            });
            return pane;
        }

        function registerPlugin(plugin, matcher){
            previewers[plugin.name] = {
                plugin  : plugin,
                matcher : matcher
            };
        }

        function unregisterPlugin(plugin){
            delete previewers[plugin.name];
        }

        function openPreview(path, pane){
            tabs.open({
                name       : "preview-" + path,
                editorType : "preview",
                pane       : pane,
                active     : true,
                document   : {
                    preview : {
                        path : path
                    }
                }
            }, function(){});
        }

        function findPreviewer(path, id){
            if (id) return previewers[id].plugin;
            else if (path) {
                for (id in previewers) {
                    if (previewers[id].matcher(path))
                        return previewers[id].plugin;
                }
            }

            id = settings.get("user/preview/@default");
            return previewers[id].plugin;
        }

        /**
         * The preview handle, responsible for managing preview plugins.
         * This is the object you get when you request the preview
         * service in your plugin.
         *
         * Example:
         *
         *     define(function(require, exports, module) {
         *         main.consumes = ["preview"];
         *         main.provides = ["myplugin"];
         *         return main;
         *
         *         function main(options, imports, register) {
         *             var preview = imports.preview;
         *
         *             var previewer = preview.findPreviewer("preview.browser");
         *         });
         *     });
         *
         * @class preview
         * @extends Plugin
         * @singleton
         */
        handle.freezePublicAPI({
            /**
             * The menu shown to select the previewer
             * @property {Menu} previewMenu
             */
            get previewMenu(){ return menu; },

            /**
             * Adds a previewer to the list of known previewers.
             *
             * *N.B. The {@link Previewer} base class already calls this method.*
             *
             * @param {Previewer} previewer  the previewer to register.
             * @private
             */
            register : registerPlugin,

            /**
             * Removes a previewer from the list of known previewers.
             *
             * *N.B. The {@link Previewer} base class already calls this method.*
             *
             * @param {Previewer} previewer  the previewer to unregister.
             * @private
             */
            unregister : unregisterPlugin,

            /**
             * Retrieves a previewer based on a file path or id.
             * @param {String} path  The path of the file that is to be previewed
             * @param {String} id    The unique name of the previewer to retrieve
             * @return {Previewer}
             */
            findPreviewer : findPreviewer,
        });

        handle.on("load", load);

        function Preview(){
            var plugin = new Editor("Ajax.org", main.consumes, extensions);
            //var emit   = plugin.getEmitter();

            var currentDocument, currentSession;
            var container, txtPreview, btnMode;

            plugin.on("draw", function(e){
                drawHandle();

                // Create UI elements
                var bar = e.tab.appendChild(new ui.vsplitbox({
                    anchors    : "0 0 0 0",
                    childNodes : [
                        new ui.hsplitbox({
                            "class"    : "toolbar-top previewbar",
                            height     : 35,
                            edge       : "4",
                            padding    : 3,
                            childNodes : [
                                new ui.button({
                                    skin    : "btn-preview-nav",
                                    skinset : "previewskin",
                                    "class" : "refresh",
                                    width   : "30",
                                    onclick : function(e){ reload(); }
                                }),
                                new ui.hsplitbox({
                                    padding    : 0,
                                    childNodes : [
                                        new ui.bar({
                                            id         : "locationbar",
                                            "class"    : "locationbar",
                                            childNodes : [
                                                new ui.textbox({
                                                    id          : "txtPreview",
                                                    class       : "ace_searchbox tb_textbox searchbox searchTxt tb_console",
                                                    value       : "",
                                                    focusselect : true
                                                }),
                                                new ui.button({
                                                    id      : "btnMode",
                                                    submenu : menu.aml,
                                                    icon    : "page_white.png",
                                                    skin    : "btn-preview-choice",
                                                    skinset : "previewskin",
                                                    caption : "browser"
                                                })
                                            ]
                                        }),
                                        new ui.button({
                                            skin    : "btn-preview-nav",
                                            skinset : "previewskin",
                                            width   : 30,
                                            class   : "popout",
                                            onclick : function(e){ popout(); }
                                        })
                                    ]
                                })
                            ]
                        }),
                        new ui.bar({
                            id : "container"
                        })
                    ]
                }));
                plugin.addElement(bar);

                btnMode    = plugin.getElement("btnMode");
                txtPreview = plugin.getElement("txtPreview");
                container  = plugin.getElement("container").$int;

                txtPreview.$input.onkeydown = function(e){
                    if (e.keyCode == 13) {
                        currentDocument.getSession().previewer.navigate({ url: this.value });
                        txtPreview.blur();
                    }
                }
            })

            /***** Method *****/

            function reload(){
                var session = currentSession;
                if (session)
                    session.previewer.reload();
            }

            function popout(){

            }

            function setPreviewer(id){
                var session = currentSession;
                if (session) {
                    // Check if previewer is available
                    if (!previewers[id])
                        return layout.showError("Could not find previewer:" + id);

                    // If this previewer is already active, do nothing
                    if (session.previewer.name == id)
                        return;

                    var doc   = currentDocument;
                    var state = plugin.getState(doc);

                    // Unload the previous previewer
                    if (session.previewer) {
                        session.cleanUp();
                        session.previewer.unloadDocument(doc);
                    }

                    // Enable the new previewer
                    var previewer = previewers[id].plugin;
                    session.previewer = previewer;

                    previewer.loadDocument(doc, plugin, state);
                    previewer.activateDocument(doc);
                    previewer.navigate({ url: session.path });
                }
            }

            function setLocation(value){
                txtPreview.setValue(value);
            }

            function setButtonStyle(caption, icon) {
                btnMode.setCaption(caption);
                btnMode.setIcon(icon);
            }

            /***** Lifecycle *****/

            plugin.on("load", function(){
            });
            plugin.on("documentLoad", function(e){
                var doc     = e.doc;
                var session = doc.getSession();

                doc.tab.backgroundColor = "rgb(41, 41, 41)";
                doc.tab.className.add("dark");

                // session.path = session.path || e.state.path;
                session.initPath = session.path || e.state.path;

                session.previewer = findPreviewer(session.initPath, (e.state || 0).previewer);
                session.previewer.loadDocument(doc, plugin);

                tabs.on("open", function(e){
                    if (!session.previewTab && e.options.path == session.path) {
                        session.previewTab = e.tab;
                        session.previewer.navigate({ url : session.path, tab: e.tab });
                    }
                }, session);
            });
            plugin.on("documentActivate", function(e){
                if (currentDocument)
                    currentSession.previewer.deactivateDocument(currentDocument);

                currentDocument = e.doc;
                currentSession  = e.doc.getSession();

                var previewer = currentSession.previewer;
                previewer.activateDocument(currentDocument);

                // @todo shouldn't previewTab be set here?
                if (currentSession.initPath) {
                    previewer.navigate({ url: currentSession.initPath });
                    delete currentSession.initPath;
                }
            });
            plugin.on("documentUnload", function(e){
                var session = e.doc.getSession();
                session.previewer.navigate(e.doc, true); // Remove the listener
                session.previewer.unloadDocument(e.doc);

                if (session == currentSession) {
                    currentDocument = null;
                    currentSession  = null;
                }
            });
            plugin.on("getState", function(e){
                var state = e.state;
                var session = e.doc.getSession();

                state.path      = session.path;
                state.previewer = session.previewer.name;

                session.previewer.getState(e.doc, state);
            });
            plugin.on("setState", function(e){
                var state   = e.state;
                var session = e.doc.getSession();

                session.path      = state.path;
                // session.previewer = state.previewer;

                session.previewer.setState(e.doc, state);
            });
            plugin.on("clear", function(){
            });
            plugin.on("focus", function(e){
                if (currentSession)
                    currentSession.previewer.focus(e);
            });
            plugin.on("blur", function(e){
                if (currentSession)
                    currentSession.previewer.blur(e);
            });
            plugin.on("enable", function(){
            });
            plugin.on("disable", function(){
            });
            plugin.on("unload", function(){
                // unload all previewers?
            });

            /***** Register and define API *****/

            /**
             * Preview pane for previewing files and content in a Cloud9 tab.
             *
             * There are a few default previewers (i.e.
             * {@link preview.browser browser}, {@link preview.raw raw},
             * {@link preview.markdown markdown}).
             *
             * It's easy to make additional previewers. See {@link Previewer}.
             *
             * Plugins can open a preview tab using the {@link tab} API:
             *
             *     tabManager.open({
             *         editorType : "preview",
             *         active     : true,
             *         document   : {
             *             preview : {
             *                 path: "https://c9.io"
             *             }
             *         }
             *     }, function(err, tab) {});
             *
             * Alternatively, use an urlView to open just the page without
             * the browser controls and URL bar:
             *
             *     tabManager.open({
             *         value      : "http://www.c9.io",
             *         editorType : "urlview",
             *         active     : true,
             *         document   : {
             *             urlview : {
             *                 backgroundColor : "#FF0000",
             *                 dark            : true
             *             }
             *         }
             *     }, function(err, tab) {})
             **/
            plugin.freezePublicAPI({
                /**
                 * The HTML element to attach your custom previewer to.
                 * @property {HTMLElement} container
                 */
                get container(){ return container; },

                /**
                 * Trigger a reload of the content displayed in the previewer.
                 */
                reload : reload,

                /**
                 * Pop the previewer out of the Cloud9 tab into a new window.
                 * @ignore Not implemented
                 */
                popout : popout,

                /**
                 * Change to a different previewer for the displayed content.
                 * @param {String} name  The name of the previewer to show (e.g. "previewer.browser").
                 */
                setPreviewer : setPreviewer,

                /**
                 * Set the value of the location bar of the preview pane.
                 * @param {String} value  The value of the location bar.
                 */
                setLocation : setLocation,

                /**
                 * Set the icon and label of the button in the preview bar that
                 * allows users to choose which previewer to use.
                 * @param {String} caption  The caption of the button.
                 * @param {String} icon     The icon of the button.
                 */
                setButtonStyle : setButtonStyle
            });

            plugin.load("preview" + counter++);

            return plugin;
        }

        register(null, {
            preview: handle
        });
    }
});<commit_msg>Fixes +219<commit_after>define(function(require, exports, module) {
    main.consumes = [
        "c9", "Editor", "editors", "util", "settings", "Menu", "ui",
        "preferences", "layout", "tabManager", "tree"
    ];
    main.provides = ["preview"];
    return main;

    // @todo - Add XML Plugin
    // @todo - Add JSON Plugin
    // @todo - Add Coffee Plugin
    // @todo - Add Jade Plugin
    // @todo - Add HTML/CSS/JS (auto-updating) Plugin
    // @todo - Add additional functionality, such as popout (levels, in editor, pane, browser pane)
    // @todo - Fix the activate/deactivate events on session. They leak / are not cleaned up

    function main(options, imports, register) {
        var Editor   = imports.Editor;
        var editors  = imports.editors;
        var c9       = imports.c9;
        var ui       = imports.ui;
        var util     = imports.util;
        var settings = imports.settings;
        var layout   = imports.layout;
        var tree     = imports.tree;
        var tabs     = imports.tabManager;
        var prefs    = imports.preferences;
        var Menu     = imports.Menu;

        var extensions = [];
        var counter    = 0;

        /***** Initialization *****/

        var handle     = editors.register("preview", "Preview",
                                           Preview, extensions);
        var handleEmit = handle.getEmitter();

        var previewers = {};
        var menu;

        function load(){
            var parent = layout.findParent({ name: "preview" });
            var button = !options.hideButton && new ui.button({
                skin     : "c9-toolbarbutton-glossy",
                "class"  : "preview",
                tooltip  : "Preview the current document",
                caption  : "Preview",
                disabled : true,
                onclick  : function() {
                    var tab = tabs.focussedTab;
                    if (tab && tab.editor.type === "preview" || !tab.path)
                        return;

                    // Find a good location to open preview side-by-side
                    var pane;
                    var otherPreview = search();
                    if (otherPreview && tab.pane != otherPreview) {
                        pane = otherPreview;
                    }
                    else {
                        var nodes = tab.pane.group;
                        if (!nodes)
                            pane = tab.pane.hsplit(true);
                        else {
                            pane = nodes[nodes.indexOf(tab.pane) === 0 ? 1 : 0];
                        }
                    }

                    // Open Preview
                    openPreview(tab.path, pane);
                }
            });
            button && ui.insertByIndex(parent, button, 10, handle);

            tabs.on("focus", function(e){
                var disabled = typeof e.tab.path != "string";
                button && button.setAttribute("disabled", disabled);
            }, handle);

            tabs.on("tabDestroy", function(e){
                if (e.last && button)
                    button.disable();
            }, handle);

            settings.on("read", function(e){
                settings.setDefaults("user/preview", [
                    ["running_app", "false"],
                    ["default", options.defaultPreviewer || "raw"]
                ]);
            }, handle);

            // Preferences
            prefs.add({
                "Run" : {
                    position : 600,
                    "Preview" : {
                        position : 200,
                        "Preview Running Apps" : {
                            type : "checkbox",
                            path : "user/preview/@running_app",
                            position : 4000
                        },
                        "Default Previewer" : {
                            type : "dropdown",
                            path : "user/preview/@default",
                            position : 5000,
                            items : [
                                // @todo this should come from plugin api
                                { caption: "Raw", value: "preview.raw" },
                                { caption: "Browser", value: "preview.browser" }
                            ]
                        },
                    }
                }
            }, handle);

            // Context menu for tree
            var itemCtxTreePreview = new apf.item({
                caption : "Preview",
                isAvailable : function(){
                    return tree.selectedNode && !tree.selectedNode.isFolder;
                },
                onclick : function(){
                    openPreview(tree.selected);
                }
            });
            tree.getElement("mnuCtxTree", function(mnuCtxTree) {
                ui.insertByIndex(mnuCtxTree, itemCtxTreePreview, 160, handle);
            });

            menu = new Menu({}, handle);
        }

        var drawn = false;
        function drawHandle(){
            if (drawn) return;
            drawn = true;

            // Import Skin
            ui.insertSkin({
                name         : "previewskin",
                data         : require("text!./skin.xml"),
                "media-path" : options.staticPrefix + "/images/",
                "icon-path"  : options.staticPrefix + "/images/"
            }, handle);

            // Import CSS
            var css = require("text!./style.css");
            ui.insertCss(css, options.staticPrefix, handle);

            handleEmit("draw", null, true);
        }

        //Search through pages
        function search(){
            var pane;
            tabs.getTabs().every(function(tab){
                if (tab.editorType == "preview") {
                    pane = tab.pane;
                    return false;
                }
                return true;
            });
            return pane;
        }

        function registerPlugin(plugin, matcher){
            previewers[plugin.name] = {
                plugin  : plugin,
                matcher : matcher
            };
        }

        function unregisterPlugin(plugin){
            delete previewers[plugin.name];
        }

        function openPreview(path, pane){
            tabs.open({
                name       : "preview-" + path,
                editorType : "preview",
                pane       : pane,
                active     : true,
                document   : {
                    preview : {
                        path : path
                    }
                }
            }, function(){});
        }

        function findPreviewer(path, id){
            if (id) return previewers[id].plugin;
            else if (path) {
                for (id in previewers) {
                    if (previewers[id].matcher(path))
                        return previewers[id].plugin;
                }
            }

            id = settings.get("user/preview/@default");
            return previewers[id].plugin;
        }

        /**
         * The preview handle, responsible for managing preview plugins.
         * This is the object you get when you request the preview
         * service in your plugin.
         *
         * Example:
         *
         *     define(function(require, exports, module) {
         *         main.consumes = ["preview"];
         *         main.provides = ["myplugin"];
         *         return main;
         *
         *         function main(options, imports, register) {
         *             var preview = imports.preview;
         *
         *             var previewer = preview.findPreviewer("preview.browser");
         *         });
         *     });
         *
         * @class preview
         * @extends Plugin
         * @singleton
         */
        handle.freezePublicAPI({
            /**
             * The menu shown to select the previewer
             * @property {Menu} previewMenu
             */
            get previewMenu(){ return menu; },

            /**
             * Adds a previewer to the list of known previewers.
             *
             * *N.B. The {@link Previewer} base class already calls this method.*
             *
             * @param {Previewer} previewer  the previewer to register.
             * @private
             */
            register : registerPlugin,

            /**
             * Removes a previewer from the list of known previewers.
             *
             * *N.B. The {@link Previewer} base class already calls this method.*
             *
             * @param {Previewer} previewer  the previewer to unregister.
             * @private
             */
            unregister : unregisterPlugin,

            /**
             * Retrieves a previewer based on a file path or id.
             * @param {String} path  The path of the file that is to be previewed
             * @param {String} id    The unique name of the previewer to retrieve
             * @return {Previewer}
             */
            findPreviewer : findPreviewer,
        });

        handle.on("load", load);

        function Preview(){
            var plugin = new Editor("Ajax.org", main.consumes, extensions);
            //var emit   = plugin.getEmitter();

            var currentDocument, currentSession;
            var container, txtPreview, btnMode;

            plugin.on("draw", function(e){
                drawHandle();

                // Create UI elements
                var bar = e.tab.appendChild(new ui.vsplitbox({
                    anchors    : "0 0 0 0",
                    childNodes : [
                        new ui.hsplitbox({
                            "class"    : "toolbar-top previewbar",
                            height     : 35,
                            edge       : "4",
                            padding    : 3,
                            childNodes : [
                                new ui.button({
                                    skin    : "btn-preview-nav",
                                    skinset : "previewskin",
                                    "class" : "refresh",
                                    width   : "30",
                                    onclick : function(e){ reload(); }
                                }),
                                new ui.hsplitbox({
                                    padding    : 0,
                                    childNodes : [
                                        new ui.bar({
                                            id         : "locationbar",
                                            "class"    : "locationbar",
                                            childNodes : [
                                                new ui.textbox({
                                                    id          : "txtPreview",
                                                    class       : "ace_searchbox tb_textbox searchbox searchTxt tb_console",
                                                    value       : "",
                                                    focusselect : true
                                                }),
                                                new ui.button({
                                                    id      : "btnMode",
                                                    submenu : menu.aml,
                                                    icon    : "page_white.png",
                                                    skin    : "btn-preview-choice",
                                                    skinset : "previewskin",
                                                    caption : "browser"
                                                })
                                            ]
                                        }),
                                        new ui.button({
                                            skin    : "btn-preview-nav",
                                            skinset : "previewskin",
                                            width   : 30,
                                            class   : "popout",
                                            onclick : function(e){ popout(); }
                                        })
                                    ]
                                })
                            ]
                        }),
                        new ui.bar({
                            id : "container"
                        })
                    ]
                }));
                plugin.addElement(bar);

                btnMode    = plugin.getElement("btnMode");
                txtPreview = plugin.getElement("txtPreview");
                container  = plugin.getElement("container").$int;

                txtPreview.$input.onkeydown = function(e){
                    if (e.keyCode == 13) {
                        currentDocument.getSession().previewer.navigate({ url: this.value });
                        txtPreview.blur();
                    }
                }
            })

            /***** Method *****/

            function reload(){
                var session = currentSession;
                if (session)
                    session.previewer.reload();
            }

            function popout(){

            }

            function setPreviewer(id){
                var session = currentSession;
                if (session) {
                    // Check if previewer is available
                    if (!previewers[id])
                        return layout.showError("Could not find previewer:" + id);

                    // If this previewer is already active, do nothing
                    if (session.previewer.name == id)
                        return;

                    var doc   = currentDocument;
                    var state = plugin.getState(doc);

                    // Unload the previous previewer
                    if (session.previewer) {
                        session.cleanUp();
                        session.previewer.unloadDocument(doc);
                    }

                    // Enable the new previewer
                    var previewer = previewers[id].plugin;
                    session.previewer = previewer;

                    previewer.loadDocument(doc, plugin, state);
                    previewer.activateDocument(doc);
                    previewer.navigate({ url: session.path });
                }
            }

            function setLocation(value){
                txtPreview.setValue(value);
            }

            function setButtonStyle(caption, icon) {
                btnMode.setCaption(caption);
                btnMode.setIcon(icon);
            }

            /***** Lifecycle *****/

            plugin.on("load", function(){
            });
            plugin.on("documentLoad", function(e){
                var doc     = e.doc;
                var session = doc.getSession();

                doc.tab.backgroundColor = "rgb(41, 41, 41)";
                doc.tab.className.add("dark");

                // session.path = session.path || e.state.path;
                session.initPath = session.path || e.state.path;

                session.previewer = findPreviewer(session.initPath, (e.state || 0).previewer);
                session.previewer.loadDocument(doc, plugin);

                tabs.on("open", function(e){
                    if (!session.previewTab && e.options.path == session.path) {
                        session.previewTab = e.tab;
                        session.previewer.navigate({ url : session.path, tab: e.tab });
                    }
                }, session);
            });
            plugin.on("documentActivate", function(e){
                if (currentDocument)
                    currentSession.previewer.deactivateDocument(currentDocument);

                currentDocument = e.doc;
                currentSession  = e.doc.getSession();

                var previewer = currentSession.previewer;
                previewer.activateDocument(currentDocument);

                // @todo shouldn't previewTab be set here?
                if (currentSession.initPath) {
                    previewer.navigate({ url: currentSession.initPath });
                    delete currentSession.initPath;
                }
            });
            plugin.on("documentUnload", function(e){
                var session = e.doc.getSession();
                session.previewer.navigate(e.doc, true); // Remove the listener
                session.previewer.unloadDocument(e.doc);

                if (session == currentSession) {
                    currentDocument = null;
                    currentSession  = null;
                }
            });
            plugin.on("getState", function(e){
                var state = e.state;
                var session = e.doc.getSession();

                state.path      = session.path;
                state.previewer = session.previewer.name;

                session.previewer.getState(e.doc, state);
            });
            plugin.on("setState", function(e){
                var state   = e.state;
                var session = e.doc.getSession();

                session.path      = state.path;
                // session.previewer = state.previewer;

                session.previewer.setState(e.doc, state);
            });
            plugin.on("clear", function(){
            });
            plugin.on("focus", function(e){
                if (currentSession)
                    currentSession.previewer.focus(e);
            });
            plugin.on("blur", function(e){
                if (currentSession)
                    currentSession.previewer.blur(e);
            });
            plugin.on("enable", function(){
            });
            plugin.on("disable", function(){
            });
            plugin.on("unload", function(){
                // unload all previewers?
            });

            /***** Register and define API *****/

            /**
             * Preview pane for previewing files and content in a Cloud9 tab.
             *
             * There are a few default previewers (i.e.
             * {@link preview.browser browser}, {@link preview.raw raw},
             * {@link preview.markdown markdown}).
             *
             * It's easy to make additional previewers. See {@link Previewer}.
             *
             * Plugins can open a preview tab using the {@link tab} API:
             *
             *     tabManager.open({
             *         editorType : "preview",
             *         active     : true,
             *         document   : {
             *             preview : {
             *                 path: "https://c9.io"
             *             }
             *         }
             *     }, function(err, tab) {});
             *
             * Alternatively, use an urlView to open just the page without
             * the browser controls and URL bar:
             *
             *     tabManager.open({
             *         value      : "http://www.c9.io",
             *         editorType : "urlview",
             *         active     : true,
             *         document   : {
             *             urlview : {
             *                 backgroundColor : "#FF0000",
             *                 dark            : true
             *             }
             *         }
             *     }, function(err, tab) {})
             **/
            plugin.freezePublicAPI({
                /**
                 * The HTML element to attach your custom previewer to.
                 * @property {HTMLElement} container
                 */
                get container(){ return container; },

                /**
                 * Trigger a reload of the content displayed in the previewer.
                 */
                reload : reload,

                /**
                 * Pop the previewer out of the Cloud9 tab into a new window.
                 * @ignore Not implemented
                 */
                popout : popout,

                /**
                 * Change to a different previewer for the displayed content.
                 * @param {String} name  The name of the previewer to show (e.g. "previewer.browser").
                 */
                setPreviewer : setPreviewer,

                /**
                 * Set the value of the location bar of the preview pane.
                 * @param {String} value  The value of the location bar.
                 */
                setLocation : setLocation,

                /**
                 * Set the icon and label of the button in the preview bar that
                 * allows users to choose which previewer to use.
                 * @param {String} caption  The caption of the button.
                 * @param {String} icon     The icon of the button.
                 */
                setButtonStyle : setButtonStyle
            });

            plugin.load("preview" + counter++);

            return plugin;
        }

        register(null, {
            preview: handle
        });
    }
});<|endoftext|>


<commit_before>export {createPromise};

/**
 * @param {string}  url      The URL of the script to be loaded.
 * @param {boolean} isModule If true, the script will be loaded as javascript module.
 * @return A promise. It resolves when the script is loaded.
 */
function createPromise(url, isModule) {
  return new Promise((resolve, reject) => {
    let attributes = { src:url, onload:e=>resolve(e), onerror:e=>reject(e) };
    if (isModule)
      attributes.type = "module";

    document.head.appendChild(Object.assign(document.createElement("script"), attributes));
  }
}
<commit_msg>Update ScriptLoader.js<commit_after>export {createPromise};

/**
 * @param {string}  url      The URL of the script to be loaded.
 * @param {boolean} isModule If true, the script will be loaded as javascript module.
 * @return A promise. It resolves when the script is loaded.
 */
function createPromise(url, isModule) {
  return new Promise((resolve, reject) => {
    let attributes = { src:url, onload:e=>resolve(e), onerror:e=>reject(e) };
    if (isModule)
      attributes.type = "module";

    document.head.appendChild(Object.assign(document.createElement("script"), attributes));
  });
}
<|endoftext|>


<commit_before>'use strict';

/**
 *  client
 **/

/**
 *  client.common
 **/

/*global $, _, nodeca, window, document*/

/**
 *  client.common.init()
 *
 *  Assigns all necessary event listeners and handlers.
 *
 *
 *  ##### Example
 *
 *      nodeca.client.common.init();
 **/
module.exports = function () {
  nodeca.io.init();

  $(function () {
    // Bootstrap.Collapse calls e.preventDefault() only when there's no
    // data-target attribute. We don't want URL to be changed, so we are
    // forcing prevention of default behavior.
    $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
      e.preventDefault();
    });

    // Bootstrap.Dropdown don't calls e.preventDefault() by default
    $('body').on('click.dropdown.data-api', '[data-toggle=dropdown]', function ( e ) {
      e.preventDefault();
    });

    //
    // Observe quicksearch focus to tweak icon style
    //

    $('.navbar-search .search-query')
      .focus(function (){ $(this).next('div').addClass('focused'); })
      .blur(function (){ $(this).next('div').removeClass('focused'); });
  });

  // history intercepts clicks on all `a` elements,
  // so we initialize it as later as possible to have
  // "lowest" priority of handlers
  nodeca.client.common.init.history();
};
<commit_msg>Revert "Prevent link processing for dropdowns"<commit_after>'use strict';

/**
 *  client
 **/

/**
 *  client.common
 **/

/*global $, _, nodeca, window, document*/

/**
 *  client.common.init()
 *
 *  Assigns all necessary event listeners and handlers.
 *
 *
 *  ##### Example
 *
 *      nodeca.client.common.init();
 **/
module.exports = function () {
  nodeca.io.init();

  $(function () {
    // Bootstrap.Collapse calls e.preventDefault() only when there's no
    // data-target attribute. We don't want URL to be changed, so we are
    // forcing prevention of default behavior.
    $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
      e.preventDefault();
    });

    //
    // Observe quicksearch focus to tweak icon style
    //

    $('.navbar-search .search-query')
      .focus(function (){ $(this).next('div').addClass('focused'); })
      .blur(function (){ $(this).next('div').removeClass('focused'); });
  });

  // history intercepts clicks on all `a` elements,
  // so we initialize it as later as possible to have
  // "lowest" priority of handlers
  nodeca.client.common.init.history();
};
<|endoftext|>


<commit_before>const render = require('node-sass').render;
const join = require('path').join;
const fs = require('fs');

module.exports = function transpile(cb) {
    render({
        file: join(__dirname, './main.scss'),
        outputStyle: 'compressed'
    }, (err, result) => {
        if(err) console.log(err);
        else {
            if (!fs.existsSync(join(__dirname, '../../build/styles'))) fs.mkdirSync(join(__dirname, '../../build/styles'));
            fs.writeFile(join(__dirname, '../../build/styles/main.css'), result.css, err => {
                if (err) console.error(err);
                if (cb) cb();
            });
        }
    });
};<commit_msg>Fix unbuilt serving<commit_after>const render = require('node-sass').render;
const join = require('path').join;
const fs = require('fs');

module.exports = function transpile(cb) {
    render({
        file: join(__dirname, './main.scss'),
        outputStyle: 'compressed'
    }, (err, result) => {
        if(err) console.log(err);
        else {
            if (!fs.existsSync(join(__dirname, '../../build'))) fs.mkdirSync(join(__dirname, '../../build'));
            if (!fs.existsSync(join(__dirname, '../../build/styles'))) fs.mkdirSync(join(__dirname, '../../build/styles'));
            fs.writeFile(join(__dirname, '../../build/styles/main.css'), result.css, err => {
                if (err) console.error(err);
                if (cb) cb();
            });
        }
    });
};<|endoftext|>


<commit_before>;
define(['epl', 'epl/Settings', 'lib/csc/Error', 'lib/knockout', 'lib/ajaxfileupload'], function (epl, Settings, Error, ko) {

return (function () {

    /**
     * Creates the ViewModel to back the Upload Story screen.
     * @return void
     */
     var UploadStoryViewModel = function () {
        var self = this;
        self.story = new Story();
        self.branchOptions = ko.observableArray([]);

        // Obtain branches for the 'Branches' drop down
        $.ajax({
            type : "GET",
            dataType : "json",
            url : Settings.apiBranchUrl,
            success : function(response) {
                self.branchOptions(response.objects);
            },
            error : function(response) {
                Error.throw(new Error('UploadStory.branchesAjax'));
            }
        });

        // In progress: Ajax call to upload the media file.
        self.submitStory = function() {
            $.ajaxFileUpload({
                url: '/upload/' + 1 + '/',
                secureuri: false,
                fileElementId: 'file',
                success: function (data, status) {
                    if(typeof(data.error) != 'undefined') {
                        if(data.error != '') {
                            alert(data.error);
                        } else {
                            alert(data.msg);
                        }
                    }
                    alert(status);
                },
                error: function (data, status, e) {
                    alert(status);
                }
            });

            /* This ajax call is working to upload a new
               story, except for the media file.
            $.ajax(Settings.apiStoryUrl, {
                data: ko.toJSON(self.story),
                type: "POST",
                contentType: "application/json",
                success: function(result) {
                    if (content_type == "media") {
                        // Upload image
                    }
                },
                error: function(result) {
                    // Handle errors
                }
            });*/
        };
    };

    function Story() {
        this.title = ko.observable();
        this.description = ko.observable();
        this.story_text = ko.observable();
        this.link_url = ko.observable();
        this.branch = ko.observable();
        this.year = ko.observable();
        this.month = ko.observable();
        this.day = ko.observable();
        this.public_approved = ko.observable();
        this.anonymous_ind = ko.observable();
        this.contentType = ko.observable();
        this.custom_keywords = ko.observable();
        this.preset_keywords = ko.observableArray([]);
        this.keywords = ko.computed(function() {
            // Returns an array of the checked predefined keywords,
            // as well as the comma separated user-entered keywords
            if (this.custom_keywords() != null) {
                var splitCustomKeywords = this.custom_keywords().split(",");
                return this.preset_keywords().concat(splitCustomKeywords);
            } else {
                return this.preset_keywords();
            }
        }, this);
    };

    // Modifies the json to be compatible with what is expected from the API
    Story.prototype.toJSON = function(){
        var copy = this;
        delete copy.custom_keywords;
        delete copy.preset_keywords
        copy.branch = Settings.apiBranchUrl + copy.branch + "/";
    	return copy;
    }

    return UploadStoryViewModel;
})();

//End module
});<commit_msg>Fixed missing semicolon<commit_after>;
define(['epl', 'epl/Settings', 'lib/csc/Error', 'lib/knockout', 'lib/ajaxfileupload'], function (epl, Settings, Error, ko) {

return (function () {

    /**
     * Creates the ViewModel to back the Upload Story screen.
     * @return void
     */
     var UploadStoryViewModel = function () {
        var self = this;
        self.story = new Story();
        self.branchOptions = ko.observableArray([]);

        // Obtain branches for the 'Branches' drop down
        $.ajax({
            type : "GET",
            dataType : "json",
            url : Settings.apiBranchUrl,
            success : function(response) {
                self.branchOptions(response.objects);
            },
            error : function(response) {
                Error.throw(new Error('UploadStory.branchesAjax'));
            }
        });

        // In progress: Ajax call to upload the media file.
        self.submitStory = function() {
            $.ajaxFileUpload({
                url: '/upload/' + 1 + '/',
                secureuri: false,
                fileElementId: 'file',
                success: function (data, status) {
                    if(typeof(data.error) != 'undefined') {
                        if(data.error != '') {
                            alert(data.error);
                        } else {
                            alert(data.msg);
                        }
                    }
                    alert(status);
                },
                error: function (data, status, e) {
                    alert(status);
                }
            });

            /* This ajax call is working to upload a new
               story, except for the media file.
            $.ajax(Settings.apiStoryUrl, {
                data: ko.toJSON(self.story),
                type: "POST",
                contentType: "application/json",
                success: function(result) {
                    if (content_type == "media") {
                        // Upload image
                    }
                },
                error: function(result) {
                    // Handle errors
                }
            });*/
        };
    };

    function Story() {
        this.title = ko.observable();
        this.description = ko.observable();
        this.story_text = ko.observable();
        this.link_url = ko.observable();
        this.branch = ko.observable();
        this.year = ko.observable();
        this.month = ko.observable();
        this.day = ko.observable();
        this.public_approved = ko.observable();
        this.anonymous_ind = ko.observable();
        this.contentType = ko.observable();
        this.custom_keywords = ko.observable();
        this.preset_keywords = ko.observableArray([]);
        this.keywords = ko.computed(function() {
            // Returns an array of the checked predefined keywords,
            // as well as the comma separated user-entered keywords
            if (this.custom_keywords() != null) {
                var splitCustomKeywords = this.custom_keywords().split(",");
                return this.preset_keywords().concat(splitCustomKeywords);
            } else {
                return this.preset_keywords();
            }
        }, this);
    };

    // Modifies the json to be compatible with what is expected from the API
    Story.prototype.toJSON = function(){
        var copy = this;
        delete copy.custom_keywords;
        delete copy.preset_keywords;
        copy.branch = Settings.apiBranchUrl + copy.branch + "/";
    	return copy;
    }

    return UploadStoryViewModel;
})();

//End module
});<|endoftext|>


<commit_before>/**
 * @file HuePicker component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import Event from '../_vendors/Event';
import Dom from '../_vendors/Dom';
import Valid from '../_vendors/Valid';

class HuePicker extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value
        };

        this.activated = false;

        this.mouseDownHandler = ::this.mouseDownHandler;
        this.mouseMoveHandler = ::this.mouseMoveHandler;
        this.mouseUpHandler = ::this.mouseUpHandler;
        this.changeHandler = ::this.changeHandler;

    }

    mouseDownHandler(e) {
        this.activated = true;
        this.changeHandler(e.pageX);
    }

    mouseMoveHandler(e) {
        if (this.activated) {
            this.changeHandler(e.pageX);
        }
    }

    mouseUpHandler() {
        this.activated = false;
    }

    changeHandler(mouseX) {

        const elOffset = Dom.getOffset(this.huePickerBarEl);
        if (!elOffset) {
            return;
        }

        const width = this.huePickerBarEl.offsetWidth,
            offsetX = Valid.range(mouseX - elOffset.left, 0, width),
            perCent = offsetX / width,
            value = perCent * 360;

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    }

    componentDidMount() {

        this.huePickerBarEl = this.refs.huePickerBar;

        Event.addEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.addEvent(document, 'mouseup', this.mouseUpHandler);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.removeEvent(document, 'mouseup', this.mouseUpHandler);
    }

    render() {

        const {className, style} = this.props,
            {value} = this.state,

            pickerClassName = classNames('hue-picker', {
                [className]: className
            }),
            pointerStyle = {
                left: `${value / 360 * 100}%`
            };

        return (

        );

    }
};

HuePicker.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * hue value (deg).
     */
    value: PropTypes.number,

    onChange: PropTypes.func

};

HuePicker.defaultProps = {

    className: null,
    style: null,

    value: 0

};

export default HuePicker;<commit_msg>update HuePicker<commit_after>/**
 * @file HuePicker component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import classNames from 'classnames';

import PureRender from '../_vendors/PureRender';
import Event from '../_vendors/Event';
import Dom from '../_vendors/Dom';
import Valid from '../_vendors/Valid';

@PureRender
class HuePicker extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: props.value
        };

        this.activated = false;

        this.mouseDownHandler = ::this.mouseDownHandler;
        this.mouseMoveHandler = ::this.mouseMoveHandler;
        this.mouseUpHandler = ::this.mouseUpHandler;
        this.changeHandler = ::this.changeHandler;

    }

    mouseDownHandler(e) {
        this.activated = true;
        this.changeHandler(e.pageX);
    }

    mouseMoveHandler(e) {
        if (this.activated) {
            this.changeHandler(e.pageX);
        }
    }

    mouseUpHandler() {
        this.activated = false;
    }

    changeHandler(mouseX) {

        const elOffset = Dom.getOffset(this.huePickerBarEl);
        if (!elOffset) {
            return;
        }

        const width = this.huePickerBarEl.offsetWidth,
            offsetX = Valid.range(mouseX - elOffset.left, 0, width),
            perCent = offsetX / width,
            value = perCent * 360;

        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    }

    componentDidMount() {

        this.huePickerBarEl = this.refs.huePickerBar;

        Event.addEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.addEvent(document, 'mouseup', this.mouseUpHandler);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousemove', this.mouseMoveHandler);
        Event.removeEvent(document, 'mouseup', this.mouseUpHandler);
    }

    render() {

        const {className, style} = this.props,
            {value} = this.state,

            pickerClassName = classNames('hue-picker', {
                [className]: className
            }),
            pointerStyle = {
                left: `${value / 360 * 100}%`
            };

        return (

        );

    }
};

if (process.env.NODE_ENV === 'development') {

    const PropTypes = require('prop-types');

    HuePicker.propTypes = {

        /**
         * The CSS class name of the root element.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the root element.
         */
        style: PropTypes.object,

        /**
         * hue value (deg).
         */
        value: PropTypes.number,

        onChange: PropTypes.func

    };

}

HuePicker.defaultProps = {
    value: 0
};

export default HuePicker;<|endoftext|>


<commit_before>/*
 * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of lwm2m-node-lib
 *
 * lwm2m-node-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * lwm2m-node-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with lwm2m-node-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var async = require('async'),
    apply = async.apply,
    coapUtils = require('./coapUtils'),
    errors = require('../../errors'),
    registry,
    config,
    logger = require('logops'),
    context = {
        op: 'LWM2MLib.DeviceManagement'
    };

/**
 * Checks if value is present
 * @param {...*} any
 * @return {Boolean}
 */
function isPresent(any) {
    return Array.prototype.slice.call(arguments)
        .reduce(function(acc, arg) { return acc && typeof arg !== 'undefined'; }, true);
}

/**
 * Execute a read operation for the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId.
 */
function read(deviceId, objectType, objectId, resourceId, callback) {
    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId
        };

        callback(null, request);
    }

    logger.debug(context, 'Reading value from resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createReadRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
    ], callback);
}

/**
 * Makes a modification over the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId, changing its value to the value passed as a parameter. The method will
 * determine whether its an Execute or a Write operation.
 *
 * @param {String} method           COAP Method to execute.
 * @param {String} deviceId         ID of the device that will be called.
 * @param {Number} objectType       Object type ID.
 * @param {Number} objectId         Object instance ID.
 * @param {Number} resourceId       Resource ID.
 * @param {String} value            Value to write.
 */
function writeResource(method, deviceId, objectType, objectId, resourceId, value, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: method,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId,
            payload: value,
            options: {
                'Content-Format': config.writeFormat
            }
        };

        callback(null, request);
    }

    function processResponse(res, callback) {
        if (res.code === '2.04') {
            callback(null, res.payload.toString('utf8'));
        } else if (res.code === '4.04') {
            callback(new errors.ObjectNotFound('/' + objectType + '/' + objectId));
        } else {
            callback(new errors.ClientError(res.code));
        }
    }

    logger.debug(context, 'Writting a new value [%s] on resource /%s/%s/%s in device [%d]',
        value, objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        processResponse
    ], callback);
}

/**
 * Makes a Write operation over the designed resource ID of the selected device.
 */
function write(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('PUT', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Makes an Execute operation over the designed resource ID of the selected device.
 */
function execute(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('POST', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Write the attributes given as a parameter in the remote resource identified by the Object type and id in the
 * selected device.
 *
 * @param {String} deviceId             ID of the device that holds the resource.
 * @param {String} objectType           ID of the object type of the instance.
 * @param {String} objectId             ID of the instance whose resource will be modified.
 * @param {String} resourceId           ID of the resource to modify.
 * @param {Object} parameters           Object with the parameters to write: each parameter is stored as an attribute.
 */
function writeAttributes(deviceId, objectType, objectId, resourceId, attributes, callback) {
    function createQueryParams(innerCb) {
        var validAttributes = ['pmin', 'pmax', 'gt', 'lt', 'st', 'cancel'],
            result = '?',
            errorList = [];

        for (var i in attributes) {
            if (attributes.hasOwnProperty(i)) {
                if (validAttributes.indexOf(i) >= 0) {
                    result += i + '=' + attributes[i] + '&';
                } else {
                    errorList.push(i);
                }
            }
        }

        if (errorList.length !== 0) {
            innerCb(new errors.UnsupportedAttributes(errorList));
        } else {
            innerCb(null, result);
        }
    }

    function createWriteAttributesRequest(data, innerCb) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+data[0].address+']' : data[0].address) +
              ':' + data[0].port,
            method: 'PUT'
        };
        if (isPresent(objectType, objectId, resourceId)) {
            request.pathname = '/' + objectType + '/' + objectId + '/' + resourceId + data[1];
        } else if (isPresent(objectType, objectId)) {
            request.pathname = '/' + objectType + '/' + objectId + data[1];
        } else {
            request.pathname = '/' + objectType + data[1];
        }

        innerCb(null, request);
    }

    logger.debug(context, 'Writting new discover attributes on resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);
    logger.debug(context, 'The new attributes are:\n%j', attributes);

    async.waterfall([
        apply(async.parallel, [
            apply(registry.get, deviceId),
            createQueryParams
        ]),
        createWriteAttributesRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.04')
    ], callback);
}

/**
 * Execute a discover operation for the selected resource, identified following the LWTM2M conventions by its:
 * deviceId, objectType, objectId and resourceId.
 */
function discover(deviceId, objectType, objectId, resourceId, fullCallback) {
    var pathname,
        trueCallback;

    if (isPresent(objectId, resourceId, fullCallback)) {
        logger.debug(context, 'Executing a resource discover operation on resource /%s/%s/%s in device [%d]',
            objectType, objectId, resourceId, deviceId);

        pathname= '/' + objectType + '/' + objectId + '/' + resourceId;
        trueCallback = fullCallback;
    } else if (isPresent(objectId, resourceId)) {
        logger.debug(context, 'Executing a instance discover operation on resource /%s/%s in device [%d]',
            objectType, objectId, deviceId);

        pathname= '/' + objectType + '/' + objectId;
        trueCallback = resourceId;
    } else {
        logger.debug(context, 'Executing a type discover operation on resource /%s in device [%d]',
            objectType, deviceId);

        pathname= '/' + objectType;
        trueCallback = objectId;
    }

    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: pathname,
            options: {
                'Accept': 'application/link-format'
            }
        };

        callback(null, request);
    }

    if (!objectType && !objectId && !resourceId) {
        logger.error(context, 'Method called with wrong number of parameters. Couldn\'t identify callback');
    } else {
        async.waterfall([
            apply(registry.get, deviceId),
            createReadRequest,
            coapUtils.sendRequest,
            coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
        ], trueCallback);
    }
}

function create(deviceId, objectType, objectId, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'POST',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId
        };

        callback(null, request);
    }

    logger.debug(context, 'Creating a new instance of object type [%s] in the device [%d] with instance id [%s]',
        objectType, deviceId, objectId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, null, '2.01')
    ], callback);
}

function remove(deviceId, objectType, objectId, callback) {
    callback(null);
}

function init(deviceRegistry, newConfig) {
    registry = deviceRegistry;
    config = newConfig;
}

exports.read = read;
exports.write = write;
exports.execute = execute;
exports.writeAttributes = writeAttributes;
exports.discover = discover;
exports.create = create;
exports.remove = remove;
exports.init = init;
<commit_msg>FIX Remove query string from pathname<commit_after>/*
 * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of lwm2m-node-lib
 *
 * lwm2m-node-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * lwm2m-node-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with lwm2m-node-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var async = require('async'),
    apply = async.apply,
    coapUtils = require('./coapUtils'),
    errors = require('../../errors'),
    registry,
    config,
    logger = require('logops'),
    context = {
        op: 'LWM2MLib.DeviceManagement'
    };

/**
 * Checks if value is present
 * @param {...*} any
 * @return {Boolean}
 */
function isPresent(any) {
    return Array.prototype.slice.call(arguments)
        .reduce(function(acc, arg) { return acc && typeof arg !== 'undefined'; }, true);
}

/**
 * Execute a read operation for the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId.
 */
function read(deviceId, objectType, objectId, resourceId, callback) {
    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId
        };

        callback(null, request);
    }

    logger.debug(context, 'Reading value from resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createReadRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
    ], callback);
}

/**
 * Makes a modification over the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId, changing its value to the value passed as a parameter. The method will
 * determine whether its an Execute or a Write operation.
 *
 * @param {String} method           COAP Method to execute.
 * @param {String} deviceId         ID of the device that will be called.
 * @param {Number} objectType       Object type ID.
 * @param {Number} objectId         Object instance ID.
 * @param {Number} resourceId       Resource ID.
 * @param {String} value            Value to write.
 */
function writeResource(method, deviceId, objectType, objectId, resourceId, value, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: method,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId,
            payload: value,
            options: {
                'Content-Format': config.writeFormat
            }
        };

        callback(null, request);
    }

    function processResponse(res, callback) {
        if (res.code === '2.04') {
            callback(null, res.payload.toString('utf8'));
        } else if (res.code === '4.04') {
            callback(new errors.ObjectNotFound('/' + objectType + '/' + objectId));
        } else {
            callback(new errors.ClientError(res.code));
        }
    }

    logger.debug(context, 'Writting a new value [%s] on resource /%s/%s/%s in device [%d]',
        value, objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        processResponse
    ], callback);
}

/**
 * Makes a Write operation over the designed resource ID of the selected device.
 */
function write(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('PUT', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Makes an Execute operation over the designed resource ID of the selected device.
 */
function execute(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('POST', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Write the attributes given as a parameter in the remote resource identified by the Object type and id in the
 * selected device.
 *
 * @param {String} deviceId             ID of the device that holds the resource.
 * @param {String} objectType           ID of the object type of the instance.
 * @param {String} objectId             ID of the instance whose resource will be modified.
 * @param {String} resourceId           ID of the resource to modify.
 * @param {Object} parameters           Object with the parameters to write: each parameter is stored as an attribute.
 */
function writeAttributes(deviceId, objectType, objectId, resourceId, attributes, callback) {
    function createQueryParams(innerCb) {
        var validAttributes = ['pmin', 'pmax', 'gt', 'lt', 'st', 'cancel'],
            result = [],
            errorList = [];

        for (var i in attributes) {
            if (attributes.hasOwnProperty(i)) {
                if (validAttributes.indexOf(i) >= 0) {
                    result.push(i + '=' + attributes[i]);
                } else {
                    errorList.push(i);
                }
            }
        }

        result = result.join('&');

        if (errorList.length !== 0) {
            innerCb(new errors.UnsupportedAttributes(errorList));
        } else {
            innerCb(null, result);
        }
    }

    function createWriteAttributesRequest(data, innerCb) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+data[0].address+']' : data[0].address) +
              ':' + data[0].port,
            method: 'PUT',
            query: data[1]
        };
        if (isPresent(objectType, objectId, resourceId)) {
            request.pathname = '/' + objectType + '/' + objectId + '/' + resourceId;
        } else if (isPresent(objectType, objectId)) {
            request.pathname = '/' + objectType + '/' + objectId;
        } else {
            request.pathname = '/' + objectType;
        }

        innerCb(null, request);
    }

    logger.debug(context, 'Writting new discover attributes on resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);
    logger.debug(context, 'The new attributes are:\n%j', attributes);

    async.waterfall([
        apply(async.parallel, [
            apply(registry.get, deviceId),
            createQueryParams
        ]),
        createWriteAttributesRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.04')
    ], callback);
}

/**
 * Execute a discover operation for the selected resource, identified following the LWTM2M conventions by its:
 * deviceId, objectType, objectId and resourceId.
 */
function discover(deviceId, objectType, objectId, resourceId, fullCallback) {
    var pathname,
        trueCallback;

    if (isPresent(objectId, resourceId, fullCallback)) {
        logger.debug(context, 'Executing a resource discover operation on resource /%s/%s/%s in device [%d]',
            objectType, objectId, resourceId, deviceId);

        pathname= '/' + objectType + '/' + objectId + '/' + resourceId;
        trueCallback = fullCallback;
    } else if (isPresent(objectId, resourceId)) {
        logger.debug(context, 'Executing a instance discover operation on resource /%s/%s in device [%d]',
            objectType, objectId, deviceId);

        pathname= '/' + objectType + '/' + objectId;
        trueCallback = resourceId;
    } else {
        logger.debug(context, 'Executing a type discover operation on resource /%s in device [%d]',
            objectType, deviceId);

        pathname= '/' + objectType;
        trueCallback = objectId;
    }

    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: pathname,
            options: {
                'Accept': 'application/link-format'
            }
        };

        callback(null, request);
    }

    if (!objectType && !objectId && !resourceId) {
        logger.error(context, 'Method called with wrong number of parameters. Couldn\'t identify callback');
    } else {
        async.waterfall([
            apply(registry.get, deviceId),
            createReadRequest,
            coapUtils.sendRequest,
            coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
        ], trueCallback);
    }
}

function create(deviceId, objectType, objectId, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'POST',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId
        };

        callback(null, request);
    }

    logger.debug(context, 'Creating a new instance of object type [%s] in the device [%d] with instance id [%s]',
        objectType, deviceId, objectId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, null, '2.01')
    ], callback);
}

function remove(deviceId, objectType, objectId, callback) {
    callback(null);
}

function init(deviceRegistry, newConfig) {
    registry = deviceRegistry;
    config = newConfig;
}

exports.read = read;
exports.write = write;
exports.execute = execute;
exports.writeAttributes = writeAttributes;
exports.discover = discover;
exports.create = create;
exports.remove = remove;
exports.init = init;
<|endoftext|>


<commit_before>import { trackPageWhenReady } from "./helpers";
import { _FlowRouter } from "./routers";
import { SETTINGS } from "./constants";

const autoRun = SETTINGS && SETTINGS.autorun !== false;

if (_FlowRouter && autoRun) {
  // something context & context.context don't exist, see: #93
  _FlowRouter.triggers.enter([(context) => {
    const page = {};

    if (context.path) {
      page.path = context.path;
    }
    if (context.context && context.context.title) {
      page.title = context.context.title;
    }

    page.url = window.location.origin + page.path;

    if (context.route && context.route.name) {
      page.name = context.route.name;
    } else {
      page.name = page.path;
    }
    if (context.context && context.context.querystring) {
      page.search = `? ${context.context.querystring}`;
    } else {
      page.search = "";
    }
    if (_FlowRouter.lastRoutePath) {
      page.referrer = window.location.origin + _FlowRouter.lastRoutePath;
    } else {
      page.referrer = document.referrer;
    }
    _FlowRouter.lastRoutePath = page.path;

    trackPageWhenReady(page.name, page);
  }]);
}
<commit_msg>Remove unnecessary array<commit_after>import { trackPageWhenReady } from "./helpers";
import { _FlowRouter } from "./routers";
import { SETTINGS } from "./constants";

const autoRun = SETTINGS && SETTINGS.autorun !== false;

if (_FlowRouter && autoRun) {
  // something context & context.context don't exist, see: #93
  _FlowRouter.triggers.enter((context) => {
    const page = {};

    if (context.path) {
      page.path = context.path;
    }
    if (context.context && context.context.title) {
      page.title = context.context.title;
    }

    page.url = window.location.origin + page.path;

    if (context.route && context.route.name) {
      page.name = context.route.name;
    } else {
      page.name = page.path;
    }
    if (context.context && context.context.querystring) {
      page.search = `? ${context.context.querystring}`;
    } else {
      page.search = "";
    }
    if (_FlowRouter.lastRoutePath) {
      page.referrer = window.location.origin + _FlowRouter.lastRoutePath;
    } else {
      page.referrer = document.referrer;
    }
    _FlowRouter.lastRoutePath = page.path;

    trackPageWhenReady(page.name, page);
  });
}
<|endoftext|>


<commit_before>;(function( window, _, ObjectFsUtils ){

    /// Variables: -------------------------------------------------------------

    var LS =    window.localStorage;

    /// Exports: ---------------------------------------------------------------

    window.ObjectFsLocalTable = connect;

    /// Functions: -------------------------------------------------------------

    function connect( name ) {

        if ( !_.isString( name ) || _.isEmpty( name )) {
            throw Error( "ObjectFsLocalTable: table name must be a non-empty string." );
        }

        name = "---" + name + "---";

        return {
            read:   read,
            write:  write,
            remove: remove,
            list:   list,
            count:  count,
            clear:  clear,
        };

        function getId( id ) {

            return name + id;
        };

        function read( id, cb ) {
            id = getId( id );

            var record = LS[ id ];
            if ( record &&  record.length ) {
                record = JSON.parse( record );
            };
            cb && cb( record !== undefined, record );
            return record;
        };

        function write( id, record, cb ) {
            id = getId( id );

            try {
                LS[ id ] = JSON.stringify( record );
                cb && cb( null, id );
                return id;
            } catch ( e ) {
                cb && cb( e, false );
                return false;
            }
        };

        function remove( id, cb ) {
            id = getId( id );

            var exists = LS[ id ] !== undefined;
            LS.removeItem( id );
            cb && cb( null, exists );
            return exists;
        };

        function list( filter, options, cb ) {

            var keys =  getKeys();
            var all =   [];

            for ( var i=0,len=keys.length; i<len; i++ ) {
                all.push( JSON.parse( LS[ keys[ i ]]));
            }

            var results = ObjectFsUtils.filterItems( all, filter, options );

            cb && cb( null, results );
            return results;
        };

        function count( filter, options, cb ) {

            var count = 0;

            if ( !filter && !options ) {
                count = getKeys().length;
            } else {
                count = list( filter, options ).length;
            }

            cb && cb( null, count );
            return count;
        };

        function clear( cb ) {

            try {
                getKeys().map( function( k ){ LS.removeItem( k )});
                cb && cb( null, true );
                return true;
            } catch ( e ) {
                cb && cb( e );
                return false;
            }
        };

        function getKeys() {

            return _.filter( Object.keys( LS ), tableKey )
        };

        function tableKey( key ) {
            return key.indexOf( name ) === 0;
        };
    };

})( window, window._, window.ObjectFsUtils );
<commit_msg>Fixed ObjectFsLocalTable.read() not to callback with error on success.<commit_after>;(function( window, _, ObjectFsUtils ){

    /// Variables: -------------------------------------------------------------

    var LS =    window.localStorage;

    /// Exports: ---------------------------------------------------------------

    window.ObjectFsLocalTable = connect;

    /// Functions: -------------------------------------------------------------

    function connect( name ) {

        if ( !_.isString( name ) || _.isEmpty( name )) {
            throw Error( "ObjectFsLocalTable: table name must be a non-empty string." );
        }

        name = "---" + name + "---";

        return {
            read:   read,
            write:  write,
            remove: remove,
            list:   list,
            count:  count,
            clear:  clear,
        };

        function getId( id ) {

            return name + id;
        };

        function read( id, cb ) {
            id = getId( id );

            var record = LS[ id ];
            if ( record === undefined ) {
                cb && cb( "Not found.", record );
            } else {
                record = JSON.parse( record );
                cb && cb( null, record );
            }
            return record;
        };

        function write( id, record, cb ) {
            id = getId( id );

            try {
                LS[ id ] = JSON.stringify( record );
                cb && cb( null, id );
                return id;
            } catch ( e ) {
                cb && cb( e, false );
                return false;
            }
        };

        function remove( id, cb ) {
            id = getId( id );

            var exists = LS[ id ] !== undefined;
            LS.removeItem( id );
            cb && cb( null, exists );
            return exists;
        };

        function list( filter, options, cb ) {

            var keys =  getKeys();
            var all =   [];

            for ( var i=0,len=keys.length; i<len; i++ ) {
                all.push( JSON.parse( LS[ keys[ i ]]));
            }

            var results = ObjectFsUtils.filterItems( all, filter, options );

            cb && cb( null, results );
            return results;
        };

        function count( filter, options, cb ) {

            var count = 0;

            if ( !filter && !options ) {
                count = getKeys().length;
            } else {
                count = list( filter, options ).length;
            }

            cb && cb( null, count );
            return count;
        };

        function clear( cb ) {

            try {
                getKeys().map( function( k ){ LS.removeItem( k )});
                cb && cb( null, true );
                return true;
            } catch ( e ) {
                cb && cb( e );
                return false;
            }
        };

        function getKeys() {

            return _.filter( Object.keys( LS ), tableKey )
        };

        function tableKey( key ) {
            return key.indexOf( name ) === 0;
        };
    };

})( window, window._, window.ObjectFsUtils );
<|endoftext|>