define(function(require, exports, module) {

var ext = require("core/ext");
var helloDep = require("./dependency.js");

module.exports = ext.register("ext/helloplugin/helloplugin", {
    name    : "cloud9-hello-plugin",
    dev     : "Ajax.org",
    type    : ext.GENERAL,
    deps    : [],
    nodes   : [],
    alone   : true,

    hook : function() {
        console.log("======>" + helloDep.getHello());
        console.log("======> hooking")
    },

    enable: function () {
                //console.log("======> hooking")
    },

    disable: function () {}
});

});
Example #2
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");

module.exports = ext.register("ext/connect/connect", {
    dev         : "Ajax.org",
    name        : "Offline",
    alone       : true,
    type        : ext.GENERAL,
    deps        : [],
    
    init : function(){
        ide.onLine = -1;
        ide.addEventListener("socketConnect", function(e){
            ide.onLine = true;

            ide.dispatchEvent("beforeonline");
            ide.dispatchEvent("afteronline");
            
            stServerConnected.activate();
            winReconnect.hide();
        });
        
        ide.addEventListener("socketDisconnect", function(e){
            ide.onLine = false;

            ide.dispatchEvent("beforeoffline");
            ide.dispatchEvent("afteroffline");
            
            stServerConnected.deactivate();
            winReconnect.show();
        });
    },
    
    enable : function(){
    },
    
    disable : function(){
    },
    
    destroy : function(){
        //Remove all events
    }
});

});
Example #3
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var themes = require("ext/themes/themes");

module.exports = ext.register("ext/themes_default/themes_default", {
    name    : "Themes",
    dev     : "Ajax.org",
    alone   : true,
    type    : ext.GENERAL,
    nodes   : [],

    themes  : {
        "TextMate" : "ace/theme/textmate",
        "Eclipse" : "ace/theme/eclipse",
        "Dawn" : "ace/theme/dawn",
        "IdleFingers" : "ace/theme/idle_fingers",
        "Twilight" : "ace/theme/twilight",
        "Monokai": "ace/theme/monokai",
        "Cobalt": "ace/theme/cobalt",
        "Mono Industrial": "ace/theme/mono_industrial",
        "Clouds": "ace/theme/clouds",
        "Clouds Midnight": "ace/theme/clouds_midnight",     
        "krTheme": "ace/theme/kr_theme"        
    },

    init : function(){
        themes.register(this.themes);
    },

    enable : function(){
    },

    disable : function(){
    },

    destroy : function(){
    }
});

});
Example #4
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");

module.exports = ext.register("ext/ganalytics/ganalytics", {
    name   : "Google Analytics",
    dev    : "Ajax.org",
    type   : ext.GENERAL,
    alone  : true,

    hook : function(){

        //Include Google analytics
        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
        apf.include(gaJsHost + "google-analytics.com/ga.js", false, null, null, function() {
            if (!self._gat) return;

            pageTracker = _gat._getTracker("UA-28921268-1");  //analytics code

            pageTracker._initData();
            ide.dispatchEvent("track_action", {type: "login"});
        });

        var pending = [];

        ide.addEventListener("socketConnect", function() {
            for (var i = 0; i < pending.length; i++)
                ide.send(pending[i]);
            pending = [];
        });

        ide.addEventListener("track_action", function(e) {
            // Google analytics action tracking
            // if (pageTracker)
            //     pageTracker._trackPageview("/" + e.type == "console" ? "cmd-" + e.cmd : e.type);

            //console.log(e.type == "console" ? "cmd-" + e.cmd : e.type);
            var stats = {
                "command": "stats",
                "type": "measure",
                "metric": e.activity || "activity",
                "title": e.type,
                "workspaceId": cloud9config.workspaceId,
                "projectName": cloud9config.projectName,
                "uid": cloud9config.uid,
                "version": cloud9config.version
            };
            // augment the [stats] object with key/values from the [e] object
            for (var i in e) {
                var type = typeof e[i];
                if (!e.hasOwnProperty(i) || stats[i] || type == "function" || i == "currentTarget" || i.indexOf("ubble") > -1)
                    continue;
                stats[i] = e[i];
            }
            if (ide.connected)
                ide.send(stats);
            else
                pending.push(stats);
        });
    }

});

});
Example #5
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var menus = require("ext/menus/menus");
var editors = require("ext/editors/editors");
var Range = require("ace/range").Range;
var markup = require("text!ext/formatjson/formatjson.xml");
var commands = require("ext/commands/commands");

module.exports = ext.register("ext/formatjson/formatjson", {
    name     : "JSON Formatter",
    dev      : "Ajax.org",
    alone    : true,
    type     : ext.GENERAL,
    markup   : markup,
    
    nodes : [],
    
    format : function(indent){
        var editor = editors.currentEditor;

        var sel   = editor.getSelection();
        var doc   = editor.getDocument();
        var range = sel.getRange();
        var value = doc.getTextRange(range);
        try {
            value = JSON.stringify(JSON.parse(value), null, indent);
        }
        catch (e) {
            util.alert(
                "Invalid JSON", 
                "The selection contains an invalid or incomplete JSON string",
                "Please correct the JSON and try again");
            return;
        }
        
        var end = doc.replace(range, value);
        sel.setSelectionRange(Range.fromPoints(range.start, end));
    },
    
    hook : function(){
        var _self = this;
        
        commands.addCommand({
            name : "formatjson",
            bindKey : {mac: "Shift-Command-J", win: "Ctrl-Shift-J"},
            hint: "reformat the current JSON document",
            isAvailable : function(editor){
                if (editor && editor.ceEditor) {
                    var range = editor.ceEditor.$editor.getSelectionRange();
                    return range.start.row == range.end.row 
                      && range.start.column == range.end.column
                }
                return false;
            },
            exec : function(){
                ext.initExtension(_self);
                _self.winFormat.show();
            }
        });
        
        var mnuItem;
        this.nodes.push(
            mnuItem = menus.addItemByPath("Tools/Format JSON", new apf.item({
                command : "formatjson"
            }), 500)
        );
    },
    
    init : function(amlNode){
        this.winFormat = winFormat;
    },
    
    enable : function(){
        this.nodes.each(function(item){
            item.enable();
        });
    },
    
    disable : function(){
        this.nodes.each(function(item){
            item.disable();
        });
    },
    
    destroy : function(){
        commands.removeCommandByName("formatjson");
        
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
        this.winFormat.destroy(true, true);
    }
});

    }
Example #6
0
define(function(require, exports, module) {

var ext = require("core/ext");
var ide = require("core/ide");
var editors = require("ext/editors/editors")
var menus = require("ext/menus/menus");
var commands = require("ext/commands/commands");
var markup = require("text!ext/insertIf/insertIf.xml");

module.exports = ext.register("ext/insertIf/insertIf", {
    name     : "Auto If Statement",
    dev      : "Ajax.org",
    alone    : true,
    deps     : [],
    type     : ext.GENERAL,
    markup   : markup,

    nodes : [],

    init : function(){
        var _self = this;
        this.winExtensionTemplate = winExtensionTemplate;
        
        commands.addCommand({
            name: "insertIf",
            hint: "I'll say something",
            msg: "Popping window!",
            bindKey: {mac: "Ctrl-i", win: "Ctrl-2"},
            isAvailable : function() {
                
                return true;    
            },
            exec: function() {
                //This calls the insertIf function.
                _self.insertIf();
                //_self.winExtensionTemplate.show()
            }
        });
        
        this.nodes.push(
            menus.addItemByPath("Edit/Insert If Statement", new apf.item({
                command : "insertIf"
            }), 5400)
        ); 
        commands.addCommand({
            name: "insertIfElse",
            hint: "I'll say something",
            msg: "Popping window!",
            bindKey: {mac: "Ctrl-Shift+i", win: "Ctrl-2"},
            isAvailable : function() {
                
                return true;    
            },
            exec: function() {
                //This calls the insertIfElse function.
                _self.insertIfElse();
                //_self.winExtensionTemplate.show()
            }
        });
        
        this.nodes.push(
            menus.addItemByPath("Edit/Insert If Else Statement", new apf.item({
                command : "insertIfElse"
            }), 5400)
        ); 
       /* Just a plain menu...
        this.nodes.push(
            menus.addItemByPath("Edit/Extension Template", new apf.item({
                onclick : function(){
                    _self.winExtensionTemplate.show();
                }
            }), 5400)
        ); */
    },

    hook : function(){
        var _self = this;
        ext.initExtension(this);
    },

    enable : function(){
        this.nodes.each(function(item){
            item.enable();
        });
    },

    disable : function(){
        this.nodes.each(function(item){
            item.disable();
        });
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
    },

     closeExtensionTemplateWindow : function(){
        this.winExtensionTemplate.hide();
     },

     insertIf : function(){
    
       var page = ide.getActivePage();
       if(!page)
        return;
            if (!editors.currentEditor.amlEditor)
                return;
            //this gets the current editor for us to use.
            var editor = editors.currentEditor.amlEditor.$editor;
            //insert the code for an if statement.
            editor.insert("if( == )\n\t{\n\n\t}");
            
        
        this.winExtensionTemplate.hide();
     },
     insertIfElse : function(){
    
       var page = ide.getActivePage();
       if(!page)
        return;
            if (!editors.currentEditor.amlEditor)
                return;
            //this gets the current editor for us to use.
            var editor = editors.currentEditor.amlEditor.$editor;
            //insert the code for an if else statement.
            editor.insert("if( == ){\n\n\t} \n \telse{     \n\t}");
            
            
        
        this.winExtensionTemplate.hide();
     }
});

});
Example #7
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var menus = require("ext/menus/menus");
var util = require("core/util");
var settings = require("core/settings");
var commands = require("ext/commands/commands");
var anims = require("ext/anims/anims");

module.exports = ext.register("ext/editors/editors", {
    name    : "Editors",
    dev     : "Ajax.org",
    alone   : true,
    type    : ext.GENERAL,
    nodes   : [],

    fileExtensions  : {},

    showTabs : true,

    register : function(oExtension){
        /*var id = "rb" + oExtension.path.replace(/\//g, "_");

        oExtension.$rbEditor = barButtons.appendChild(new apf.radiobutton({
            id        : id,
            label     : oExtension.name,
            value     : oExtension.path,
            margin    : "0 -1 0 0",
            visible   : "{require('ext/editors/editors').isEditorAvailable(tabEditors.activepage, '" + oExtension.path + "')}",
            onclick   : function(){
                require('ext/editors/editors').switchEditor(this.value);
            }
        }));*/

        var _self = this;

        //Add a menu item to the list of editors
        oExtension.$itmEditor = menus.addItemByPath("View/Editors/" + oExtension.name, new apf.item({
            type     : "radio",
            value    : oExtension.path,
            group    : this.$itmGroup,
            onclick  : function(){
                _self.switchEditor(this.value);
            }
        }), 40000);

        var _self = this;
        oExtension.fileExtensions.each(function(mime){
            (_self.fileExtensions[mime] || (_self.fileExtensions[mime] = [])).push(oExtension);
        });

        if (!this.fileExtensions["default"] || (oExtension.name && oExtension.name == "Code Editor"))
            this.fileExtensions["default"] = oExtension;
    },

    unregister : function(oExtension){
        //oExtension.$rbEditor.destroy(true, true);
        oExtension.$itmEditor.destroy(true, true);

        var _self = this;
        oExtension.fileExtensions.each(function(fe){
            _self.fileExtensions[fe].remove(oExtension);
            if (!_self.fileExtensions[fe].length)
                delete _self.fileExtensions[fe];
        });

        menus.remove("View/Editors/" + oExtension.name);

        if (this.fileExtensions["default"] == oExtension) {
            delete this.fileExtensions["default"];

            for (var prop in this.fileExtensions) {
                this.fileExtensions["default"] = this.fileExtensions[prop][0];
                break;
            }
        }
    },

    toggleTabs : function(force, preview, noAnim, mouse){
        if (!force || force > 0) {
            if (!preview) {
                settings.model.setQueryValue("auto/tabs/@show", "true");
                this.showTabs = true;
                ide.dispatchEvent("tabs.visible", {value: true, noanim: noAnim});
            }

            this.setTabResizeValues(tabEditors.parentNode.$ext, force == 1, !noAnim, mouse, 1);
        }
        else {
            if (!preview) {
                settings.model.setQueryValue("auto/tabs/@show", "false");
                this.showTabs = false;
                ide.dispatchEvent("tabs.visible", {value: false, noanim: noAnim});
            }

            this.setTabResizeValues(tabEditors.parentNode.$ext, force == 1, !noAnim, mouse, 0);
        }

    },

    addTabSection : function(){
        var _self = this;

        var btn, btnMenu;
        var tab = new apf.bar({
            skin     : "basic",
            "class"  : "codeditorHolder",
            style    : "position:absolute;", 
            childNodes: [
                new apf.tab({
                    id      : "tabEditors",
                    skin    : "editor_tab",
                    style   : "height : 100%",
                    buttons : "close,scale,order",
                    animate : apf.isGecko
                        ? false
                        : "[{require('core/settings').model}::general/@animateui]",
                    anims   : "{apf.isTrue(this.animate) ? 'add|remove|sync' : ''}",
                    overactivetab  : true,
                    onfocus        : function(e){
                        _self.switchfocus(e);
                    },
                    onbeforeswitch : function(e){
                        _self.beforeswitch(e);
                    },
                    onafterswitch : function(e){
                        _self.afterswitch(e);
                    },
                    onclose : function(e){
                        if (!ide.onLine && !ide.offlineFileSystemSupport) //For now prevent tabs from being closed
                            return false;

                        if (this.length == 1) {
                            btn.$ext.style.position = "absolute";
                            btn.$ext.style.right = "5px";
                            btn.$ext.style.top = "6px";
                            btn.$ext.parentNode.style.overflow = "hidden";
                        }

                        e.page.addEventListener("afterclose", _self.$close);
                    },
                    childNodes : [
                        btn = new apf.button({
                            id : "plus_tab_button",
                            "class" : "plus_tab_button",
                            skin : "btn_icon_only",
                            style : "position:absolute;right:5px;top:6px",
                            onclick : function(){
                                require("ext/newresource/newresource").newfile();
                            }
                        })
                    ]
                }),

                btnMenu = new apf.button({
                    onmouseover : function(){
                        this.setAttribute("submenu", require('ext/menus/menus').getMenuId('View/Tabs'));
                    },
                    onmousedown : function(){
                        apf.setStyleClass(window[menus.getMenuId('View/Tabs')].$ext, 'tabsContextMenu');
                    },
                    skin : "btn_icon_only",
                    "class" : "tabmenubtn",
                }) /*,
                new apf.hbox({
                    id      : "barButtons",
                    edge    : "0 0 0 6",
                    "class" : "relative",
                    zindex  : "1000",
                    bottom  : "0",
                    left    : "0",
                    right   : "0"
                })*/
            ]
        });

        colMiddle.appendChild(tab);
        var tabs = tabEditors;

        this.buttons = {
            add: btn,
            menu: btnMenu
        }

        tabs.$buttons.appendChild(btn.$ext);
        tabs.$buttons.appendChild(btnMenu.$ext);
        tabs.addEventListener("DOMNodeInserted",function(e){
            if (e.$isMoveWithinParent) {
                //record position in settings

                var amlNode = e.currentTarget;
                if (amlNode.localName != "page" || e.relatedNode != this || amlNode.nodeType != 1)
                    return;

                settings.save();
            }

            if (e.relatedNode == this && e.currentTarget.localName == "page") {
                tabs.$buttons.appendChild(btn.$ext);
                if (btn.$ext.style.position) {
                    //tabs.appendChild(btn);
                btn.$ext.style.position = "";
                btn.$ext.style.right = "";
                btn.$ext.style.top = "";
            }
            }
        });

        barButtonContainer.$int.appendChild(tabEditors.$buttons);
        barButtonContainer.$int.style.paddingRight
            = (parseInt(apf.getStyle(tabEditors.$buttons, "paddingLeft"))
            + parseInt(apf.getStyle(tabEditors.$buttons, "paddingRight"))) + "px";

        if (!apf.isGecko) {
            tabEditors.$buttons.style.position = "absolute";
            tabEditors.$buttons.style.left = "0";
            tabEditors.$buttons.style.top = "0";
            tabEditors.$buttons.style.right = "0";
            tabEditors.$buttons.style.bottom = "0";
        }
    },

    /**
     * This method has been abstracted so it can be used by
     * the zen extension to get the destination coordinates and
     * dimensions of tabEditors.parentNode when the editor goes
     * out of zen mode
     */
    setTabResizeValues : function(ext, preview, animate, mouse, dir) {
        var ph;
        var _self = this;

        if (this.animating && (!animate || this.animating[0] == preview))
            return;

        if (animate) {
            if (this.animateControl)
                this.animateControl.stop();

            this.animating = [preview];

            if (dir == undefined)
                dir = tabEditors.$buttons.style.height == "10px" ? 1 : 0;
            var duration = mouse ? 0.2 : 0.2;

            tabEditors.$buttons.style.overflow = "hidden";

            if (dir) {
                apf.setStyleClass(tabEditors.$buttons.parentNode, "", ["hidetabs"]);
                apf.setStyleClass(tabEditors.$buttons.parentNode, "step5");
            }
            else {
                //@todo this is a bit hacky
                ide.dispatchEvent("animate", {
                    type: "editor",
                    delta: 16
                });
            }

            var i = dir ? 6 : 0, j = 0;
            [1,2,3,4,5,6].forEach(function(x){
                setTimeout(function(){
                    if (x == 6) {
                        if (!dir)
                            apf.setStyleClass(tabEditors.$buttons.parentNode, "hidetabs");

                        return;
                    }
                    
                    apf.setStyleClass(tabEditors.$buttons.parentNode,
                        "step" + (dir ? --i : ++i),
                        ["step" + (dir ? i + 1 : i-1)]);

                }, ++j * (duration / 6) * 1000);
            });
            
            anims.animateMultiple([
                { duration : duration, node: ext, top : (this.showTabs || preview ? 0 : -16) + "px"},
                //{ duration : duration, node: ext, height : ((this.showTabs || preview ? 0 : 16) + ph.offsetHeight - d[1]) + "px"},
                { duration : duration, node: tabEditors.$buttons, height: (this.showTabs || preview ? 22 : 7) + "px"},
                { duration : duration, node: this.buttons.add, opacity : dir ? 1 : 0},
                { duration : duration, node: this.buttons.add, height : (dir ? 17 : 10) + "px"},
                { duration : duration, node: this.buttons.menu, opacity : dir ? 1 : 0},
                { duration : duration, node: this.buttons.menu, height : (dir ? 17 : 10) + "px"}
            ], function(e){
                apf.setStyleClass(tabEditors.$buttons.parentNode, "", ["step" + i]);
                    _self.animating = false;
                
                tabEditors.parentNode.setAttribute("margin", 
                    (this.showTabs || preview ? "0 0 0 0" : "-16 0 0 0"));

                tabEditors.$buttons.style.overflow = "";
            });
        }
        else {
            if (this.showTabs || preview) {
                tabEditors.$buttons.style.height = "22px";
                apf.setStyleClass(tabEditors.$buttons.parentNode, "", ["hidetabs"]);
                this.buttons.menu.setHeight(17);
                this.buttons.add.setHeight(17);
                
                tabEditors.parentNode.setAttribute("margin", "0 0 0 0");
            }
            else {
                tabEditors.$buttons.style.height = "7px";
                apf.setStyleClass(tabEditors.$buttons.parentNode, "hidetabs");
                this.buttons.menu.setHeight(10);
                this.buttons.add.setHeight(10);
                
                tabEditors.parentNode.setAttribute("margin", "-16 0 0 0");
            }
        }
    },

    isEditorAvailable : function(page, path){
        var editor = ext.extLut[path];
        if (!editor)
            return false;

        var fileExtensions = editor.fileExtensions;
        var fileExtension = (tabEditors.getPage(page).$model.queryValue("@path") || "").split(".").pop().toLowerCase();
        var isEnabled = fileExtensions.indexOf(fileExtension) > -1;

        if (!isEnabled && this.fileExtensions["default"] == editor)
            return true;
        else
            return isEnabled;
    },

    initEditor : function(editor){
        //Create Page Element
        var editorPage = new apf.page({
            id        : editor.path,
            mimeTypes : editor.fileExtensions,
            visible   : false,
            realtime  : false
        });
        tabEditors.appendChild(editorPage);

        //Initialize Content of the page
        ext.initExtension(editor, editorPage);

        return editorPage;
    },

    switchEditor : function(path){
        var page = tabEditors.getPage();
        if (!page || page.type == path)
            return;

        var lastType = page.type;

        var info;
        if ((info = page.$doc.dispatchEvent("validate", info)) === true) {
            util.alert(
                "Could not switch editor",
                "Could not switch editor because this document is invalid.",
                "Please fix the error and try again:" + info
            );
            return;
        }

        var editor = ext.extLut[path];
        if (!editor.inited)
            this.initEditor(editor);

        //editor.$rbEditor.select();

        page.setAttribute("type", path);

        page.$editor = editor;
        this.currentEditor = editor;

        this.beforeswitch({nextPage: page});
        this.afterswitch({nextPage: page, previousPage: {type: lastType}, keepEditor : true});
    },

    openEditor : function(doc, init, active, forceOpen) {
        var xmlNode  = doc.getNode();
        var filepath = xmlNode.getAttribute("path");
        var tabs = tabEditors;

        if (!forceOpen) {
            var page = tabs.getPage(filepath);
            if (page) {
                tabs.set(page);
                return;
            }
        }

        var fileExtension = (xmlNode.getAttribute("path") || "")
            .split(".").pop().toLowerCase();
        var editor = (this.fileExtensions[fileExtension]
          && this.fileExtensions[fileExtension][0])
          || this.fileExtensions["default"];

        if (!init && this.currentEditor)
            this.currentEditor.disable();

        if (!editor) {
            util.alert(
                "No editor is registered!",
                "Could not find an editor to display content",
                "There is something wrong with the configuration of your IDE. No editor plugin is found");
            return;
        }

        if (!editor.inited)
            this.initEditor(editor);

        //Create Fake Page
        if (init)
            tabs.setAttribute("buttons", "close");

        if (!apf.isGecko)
            tabEditors.$buttons.style.overflow = "";

        var model = new apf.model();
        var fake = tabs.add("{([@changed] == 1 ? '*' : '') + [@name]}", filepath, editor.path, null, function(page){
            page.$at     = new apf.actiontracker();
            page.$doc    = doc;
            doc.$page    = page;
            page.$editor = editor;
            page.setAttribute("autofocus", false);
            page.setAttribute("tooltip", "[@path]");
            page.setAttribute("class",
                "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \
                ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}"
            );
            page.setAttribute("model", page.$model = model);

            model.load(xmlNode);
            model.addEventListener("update", function(){
                settings.save();
            });
        });

        if (init)
            tabs.setAttribute("buttons", "close,scale,order");

        doc.addEventListener("setnode", function(e) {
            fake.$model.load(e.node);
        });

        this.initEditorEvents(fake, model);

        ide.dispatchEvent("tab.create", {
            page: fake,
            model: model,
            doc: doc
        });

        if (active === false) // init && !
            return {editor: editor, page: fake};

        //Set active page
        tabs.set(filepath);

        editor.enable();
        //editor.$rbEditor.select();

        this.currentEditor = editor;

        // okay don't know if you would want this, but this is the way the 'open file' dialog
        // handles it so let's do that
        setTimeout(function () {
            if (typeof editor.amlEditor !== "undefined") {
                editor.amlEditor.focus();
                ide.dispatchEvent("aftereditorfocus");
            }
        }, 100);

        settings.save();
        
        return {editor: editor, page: fake};
    },

    initEditorEvents: function(page, model) {
        model = model || page.$model;
        page.$at.addEventListener("afterchange", function(e) {
            if (e.action == "reset") {
                delete this.undo_ptr;
                return;
            }

            var val;
            if (page.$at.ignoreChange) {
                val = undefined;
                page.$at.ignoreChange = false;
            }
            else if(this.undolength === 0 && !this.undo_ptr) {
                val = undefined;
            }
            else {
                val = (this.$undostack[this.$undostack.length - 1] !== this.undo_ptr)
                    ? 1
                    : undefined;
            }

            if (page.changed !== val) {
                page.changed = val;
                model.setQueryValue("@changed", (val ? "1" : "0"));

                var node = page.$model.data;
                ide.dispatchEvent("updatefile", {
                    changed : val ? 1 : 0,
                    xmlNode : node,
                    newPath: e.newPath
                });
            }
        });
    },

    resizeTabs : function(cancel){
        clearTimeout(this.closeTimer);

        if (cancel)
            return;

        this.closeTimer = setTimeout(function(){
            tabEditors.$waitForMouseOut = false;
            tabEditors.$scaleinit(null, "sync");
        }, 500);
    },

    close : function(page) {
        var pages = tabEditors.getPages();
        var isLast = (pages[pages.length - 1] == page);
        tabEditors.remove(page);
        this.resizeTabs(isLast);
    },

    $close : function() {
        var page   = this;
        var at     = page.$at;
        var editor = page.$editor;
        var mdl    = page.$model;

        mdl.setQueryValue("@changed", 0);
        page.$doc.dispatchEvent("close");

        if (mdl.data) {
            mdl.removeXml("data");
            ide.dispatchEvent("closefile", {xmlNode: mdl.data, page: page});
        }

        //mdl.unshare();
        mdl.destroy();

        at.reset();
        at.destroy();

        //If there are no more pages left, reset location
        if (tabEditors.getPages().length == 1) {
            /*if (window.history.pushState) {
                var p = location.pathname.split("/");
                window.history.pushState(path, path, "/" + (p[1] || "") + "/" + (p[2] || ""));
            }
            else {
                apf.history.setHash("");
            }*/
            //apf.history.setHash("");

            editor.clear && editor.clear();
            require("ext/editors/editors").currentEditor = null;
        }

        //Destroy the app page if it has no application instance
        //if (!tabEditors.selectNodes("page[@type='" + page.type + "']").length && editorPage)
            //editorPage.destroy(true, true);

        settings.save();
    },

    switchfocus : function(e){

    },

    beforeswitch : function(e) {
        var page       = e.nextPage;
        var editorPage = tabEditors.getPage(page.type);
        
        if (!editorPage) return;

        // fire this event BEFORE editor sessions are swapped.
        if (ide.dispatchEvent("tab.beforeswitch", {
            previousPage: e.previousPage,
            nextPage: e.nextPage
        }) === false)
            return false;

        if (editorPage.model != page.$model)
            editorPage.setAttribute("model", page.$model);
        if (editorPage.actiontracker != page.$at)
            editorPage.setAttribute("actiontracker", page.$at);

            page.$editor.setDocument && page.$editor.setDocument(page.$doc, page.$at);
    },

    afterswitch : function(e) {
        var _self = this;
        var page = e.nextPage;
        
        if (this.switchLoop == page.id)
            return;
        
        var fromHandler, toHandler = ext.extLut[page.type];

        if (e.previousPage && e.previousPage != e.nextPage)
            fromHandler = ext.extLut[e.previousPage.type];

        if (fromHandler != toHandler) {
            if (fromHandler)
                fromHandler.disable();
            toHandler.enable();
        }

        var path = page.$model.data.getAttribute("path").replace(/^\/workspace/, "");
        /*if (window.history.pushState) {
            var p = location.pathname.split("/");
            window.history.pushState(path, path, "/" + (p[1] || "name") + "/" + (p[2] || "project") + path);
        }
        else {
            apf.history.setHash("!" + path);
        }*/
        apf.history.setHash("!" + path);

        settings.save();

        if (!e.keepEditor) {
            var fileExtension = (path || "").split(".").pop().toLowerCase();
            var editor = (this.fileExtensions[fileExtension]
              && this.fileExtensions[fileExtension][0])
              || this.fileExtensions["default"];

            if (!editor) {
                util.alert(
                    "No editor is registered",
                    "Could not find an editor to display content!",
                    "There is something wrong with the configuration of your IDE. No editor plugin is found.");
                return;
            }

            if (!editor.inited)
                this.initEditor(editor);

            this.currentEditor = editor;
        }
        else {
            var editor = page.$editor;
        }

        if (editor.focus)
            editor.focus();

        //toHandler.$rbEditor.select();

        /*if (self.TESTING) {}
            //do nothing
        else if (page.appid)
            app.navigateTo(page.appid + "/" + page.id);
        else if (!page.id)
            app.navigateTo(app.loc || (app.loc = "myhome"));*/

        clearTimeout(this.afterswitchTimeout);
        this.afterswitchTimeout = setTimeout(function(){
            _self.switchLoop = page.id;
            
            ide.dispatchEvent("tab.afterswitch", {
                previousPage: e.previousPage,
                nextPage: e.nextPage
            });
            
            delete _self.switchLoop;
        }, 150);
    },

    /**** Init ****/

    init : function(){
        var _self = this;

        window.onpopstate = function(e){
            var page = "/workspace" + e.state;
            if (tabEditors.activepage != page && tabEditors.getPage(page))
                tabEditors.set(page);
        };

        apf.addEventListener("hashchange", function(e){
            var page = "/workspace" + e.page;
            if (tabEditors.activepage != page && tabEditors.getPage(page))
                tabEditors.set(page);
        });

        menus.addItemByPath("View/Editors/", new apf.menu({
            "onprop.visible" : function(e){
                if (e.value) {
                    if (!_self.currentEditor)
                        this.disable();
                    else {
                        this.enable();

                        var nodes = this.childNodes;
                        for (var i = nodes.length - 1; i >= 0; i--) {
                            nodes[i].setAttribute("disabled",
                                !_self.isEditorAvailable(
                                    tabEditors.activepage, nodes[i].value));
                        }

                        _self.$itmGroup.setValue(_self.currentEditor.path);
                    }
                }
            }
        }), 190);

        commands.addCommand({
            name: "toggleTabs",
            bindKey : { mac : "Ctrl-M", win : "Ctrl-M" },
            exec: function(e){
                 _self.toggleTabs(!_self.showTabs ? 1 : -1);
            }
        });

        commands.addCommand({
            name: "largerfont",
            bindKey : { mac : "Ctrl-Shift-.", win : "Ctrl-Shift-." },
            exec: function(e){
                var currSize = settings.model.queryValue("editors/code/@fontsize");
                settings.model.setQueryValue("editors/code/@fontsize", ++currSize > 72 ? 72 : currSize);
            }
        });

        commands.addCommand({
            name: "smallerfont",
            bindKey : { mac : "Ctrl-Shift-,", win : "Ctrl-Shift-," },
            exec: function(e) {
                var currSize = settings.model.queryValue("editors/code/@fontsize");
                settings.model.setQueryValue("editors/code/@fontsize", --currSize < 1 ? 1 : currSize);
            }
        });

        menus.addItemByPath("View/Font Size/", null, 199),
        
        menus.addItemByPath("View/Font Size/Increase Font Size", new apf.item({
            command : "largerfont"
        }), 1);
        
        menus.addItemByPath("View/Font Size/Decrease Font Size", new apf.item({
            command : "smallerfont"
        }), 2);
        
        menus.addItemByPath("View/Tab Buttons", new apf.item({
            type: "check",
            checked : "[{require('core/settings').model}::auto/tabs/@show]",
            command : "toggleTabs"
        }), 300);

        ext.addType("Editor", function(oExtension){
            _self.register(oExtension);
          }, function(oExtension){
            _self.unregister(oExtension);
          });

        ide.addEventListener("filenotfound", function(e) {
            var page = tabEditors.getPage(e.path);
            if (page)
                tabEditors.remove(page);
        });

        ide.addEventListener("afteroffline", function(e){
            tabEditors.$setStyleClass(tabEditors.$ext, "offline");
        });

        ide.addEventListener("afteronline", function(e){
            tabEditors.$setStyleClass(tabEditors.$ext, "", ["offline"]);
        });

        this.$itmGroup = new apf.group();

        this.nodes.push(this.addTabSection());

        ide.addEventListener("animate", function(e){
            if (self.logobar && e.which == logobar) {
                if (e.options.height == "12px") {
                    anims.animate(tabEditors.$buttons, {
                        paddingRight: "53px",
                        timingFunction: e.options.timingFunction,
                        duration: e.options.duration
                    }, function(){
                        apf.setStyleClass(tabEditors.$buttons, "morepadding");
                    });
                }
                else {
                    anims.animate(tabEditors.$buttons, {
                        paddingRight: "4px",
                        timingFunction: e.options.timingFunction,
                        duration: e.options.duration
                    }, function(){
                        apf.setStyleClass(tabEditors.$buttons, "", ["morepadding"]);
                    });
                }
            }
        });

        /**** Support for state preservation ****/

        this.$settings = {};
        ide.addEventListener("settings.load", function(e){
            settings.setDefaults("auto/files", [])
            settings.setDefaults("auto/tabs", [["show", "true"]]);

            _self.loadedSettings = false;

            if (apf.isTrue(e.model.queryValue("auto/menus/@minimized"))) {
                apf.setStyleClass(tabEditors.$buttons, "morepadding");
            }

            var showTab = settings.model.queryValue("auto/tabs/@show");
            _self.showTabs = apf.isTrue(showTab);
            if (!_self.showTabs)
                _self.toggleTabs(_self.showTabs ? 1 : -1, true, true);;

            function checkExpand(path, doc) {
                ide.addEventListener("init.ext/tree/tree", function(){
                    var parent_path = (apf.getDirname(path) || "").replace(/\/$/, "");
                    var expandEventListener = function(e) {
                        if (e.xmlNode && e.xmlNode.getAttribute("path") == parent_path) {
                            // if the file has been loaded from the tree
                            if (doc.getNode().getAttribute("newfile") != 1) {
                                // databind the node from the tree to the document
                                doc.setNode(e.xmlNode.selectSingleNode("node()[@path='" + path + "']"));
                            }
                            else {
                                // if not? then keep it this way, but invoke setNode() anyway because
                                // it triggers events
                                doc.setNode(doc.getNode());
                            }
                            trFiles.removeEventListener("expand", expandEventListener);
                        }
                    };

                    trFiles.addEventListener("expand", expandEventListener);
                });
            }

            var model = e.model;
            ide.addEventListener("extload", function(){
                // you can load a file from the hash tag, if that succeeded then return
                var loadFileFromHash =  (_self.loadFileFromHash(window.location.hash, checkExpand));
                if (loadFileFromHash) {
                    window.location.hash = loadFileFromHash; // update hash
                    return;
                }

                // otherwise, restore state from the .config file
                var active = model.queryValue("auto/files/@active");
                var nodes  = model.queryNodes("auto/files/file");

                for (var i = 0, l = nodes.length; i < l; i++) {
                    var node  = nodes[i];
                    var state = node.getAttribute("state");
                    var doc   = ide.createDocument(node);

                    // for some reason c9local can aggresively cache open files; this prevents
                    // open files from one workspace appearing in another
                    if (ide.local) {
                        if (node.getAttribute("path").split("/")[2] !== ide.workspaceId)
                            continue;
                    }
                    
                    try {
                        if (state)
                            doc.state = JSON.parse(state);
                    }
                    catch (ex) {}

                    // node.firstChild is not always present (why?)
                    if ((node.getAttribute("changed") == 1) && node.firstChild) {
                        doc.cachedValue = node.firstChild.nodeValue
                            .replace(/\n]\n]/g, "]]")
                            .replace(/\\r/g, "\r")
                            .replace(/\\n/g, "\n");
                    }

                    _self.gotoDocument({
                        doc      : doc,
                        init     : true,
                        type     : doc.state && doc.state.type,
                        forceOpen: true,
                        active   : active
                            ? active == node.getAttribute("path")
                            : i == l - 1,
                        origin: "settings"
                    });

                    if (doc.state && doc.state.type != "nofile")
                        checkExpand(node.getAttribute("path"), doc);
                }

                _self.loadedSettings = true;
            });
        });

        ide.addEventListener("settings.save", function(e){
            if (!e.model.data || !_self.loadedSettings)
                return;

            var pNode   = e.model.data.selectSingleNode("auto/files");
            var pages   = tabEditors.getPages();

            if (pNode) {
                pNode.parentNode.removeChild(pNode);
                pNode = null;
            }

            if (pages.length) {
                var active = tabEditors.activepage;
                var page   = tabEditors.getPage();
                
                if (page && page.$model.data.getAttribute("ignore") !== "1")
                    e.model.setQueryValue("auto/files/@active", active);

                pNode = apf.createNodeFromXpath(e.model.data, "auto/files");
                for (var i = 0, l = pages.length; i < l; i++) {
                    if (!pages[i] || !pages[i].$model || pages[i].$model.data.getAttribute("ignore") == "1")
                        continue;

                    var file = pages[i].$model.data;
                    if (!file || file.getAttribute("debug"))
                        continue;

                    var copy = apf.xmldb.cleanNode(file.cloneNode(false));
                    if (!copy.getAttribute("newfile"))
                        copy.removeAttribute("changed");
                    copy.removeAttribute("loading");
                    copy.removeAttribute("saving");
                    pNode.appendChild(copy);

                    var state = pages[i].$editor.getState && pages[i].$editor.getState(pages[i].$doc);
                    if (state)
                        copy.setAttribute("state", JSON.stringify(state));

                    //@todo the second part of this if can be removed as soon
                    //as the collab team implements stored changed settings
                    //please note that for this to work on loadsettings we
                    //should check whether the file on disk has changed and
                    //popup a file watch dialog to ask if the user wants to
                    //load the new file from disk, losing changes.
                    if (copy.getAttribute("changed") == 1 && copy.getAttribute("newfile") == 1) {
                        copy.appendChild(copy.ownerDocument.createCDATASection(
                            (pages[i].$doc.getValue() || "")
                                .replace(/\r/g, "\\r")
                                .replace(/\n/g, "\\n")
                                .replace(/\]\]/g, "\n]\n]")
                        ));
                    }
                }
            }
        });
    },

    /** Load any file from the hash, with optional some lines selected
     *
     * @param {string} hash Hash as obtained from the window element
     * @param {function} checkExpand Function that expands the tree for the given file
     * @return {string} The new hash
     */
    loadFileFromHash : function (hash, checkExpand) {
        // an initial state can be sent in the hash
        // match 'openfile-',
        // match any character except :& or end of file
        // optional: match : digit - digit
        // [1] is filename, [2] is starting line number, [3] is ending line number
        var editorInitialStatePattern = /openfile-(.[^:&$]*)(?:\:(\d+)-(\d+))?/;
        var rawState = hash.match(editorInitialStatePattern);

        if (rawState) {
            // build the real path, as the one in the hash is relative
            var path = ide.davPrefix.replace(/\/$/, "") + "/" + rawState[1];
            // require here is necessary for c9local, please do not change
            var doc = ide.createDocument(require("ext/filesystem/filesystem").createFileNodeFromPath(path));

            // if selection information was added, add that to the state
            if (rawState[2] && rawState[3]) {
                doc.state = {
                    scrollleft: 0, scrolltop: 0,
                    selection: {
                        start: { row: parseInt(rawState[2] || 0, 10) - 1, column: 0 },
                        end: { row: parseInt(rawState[3] || 0, 10), column: 0 } // plus 1 to capture whole previous line
                    }
                };
            }

            // send it to the dispatcher
            this.gotoDocument({
                doc    : doc,
                active : true,
                origin : "hash"
            });
            
            // and expand the tree
            checkExpand(path, doc);

            // return the new hash
            return hash.replace(editorInitialStatePattern, "");
        }

        return null;
    },

    pauseTabResize : function(){
        tabEditors.setAttribute("buttons", "close,order");
    },

    continueTabResize : function(){
        setTimeout(function(){
            tabEditors.setAttribute("buttons", "close,scale,order");
            tabEditors.$waitForMouseOut = false;
            tabEditors.$scaleinit(null, "sync");
        }, 300);
    },

    gotoDocument : function(options) {
        // require here is necessary for c9local, please do not change
        if (!options.node && options.path)
            options.node = require("ext/filesystem/filesystem").createFileNodeFromPath(options.path);

        this.jump(options);
    },


    jump : function(options) {
        var row     = options.row;
        var column  = options.column || 0;
        var text    = options.text;
        var page    = options.page;
        
        var hasData;
        if (!options.doc) {
            var node    = options.node;
            var path    = node.getAttribute("path");
            var tabs    = tabEditors;
            
            hasData = page && (tabs.getPage(path) || { }).$doc ? true : false;
        }
        
        var _self   = this;

        if (row !== undefined) {
            var jumpTo = function(){
                var f;
                setTimeout(f = function() {
                    // TODO move this to the editor
                    var editor = _self.currentEditor.amlEditor;
                    editor.$editor.gotoLine(row, column, options.animate !== false);
                    if (text)
                        editor.$editor.session.highlight(text);

                    editor.focus();
                    ide.dispatchEvent("aftereditorfocus");
                }, 1); 
            };

            if (hasData) {
                tabs.set(path);
                jumpTo();
            }
            else
                ide.addEventListener("afteropenfile", function(e) {
                    var node = e.doc.getNode();

                    if (node.getAttribute("path") == path) {
                        ide.removeEventListener("afteropenfile", arguments.callee);
                        jumpTo();
                    }
                });
        }

        if (!hasData) {
            options.origin = "jump";
            if (!options.doc)
                options.doc = ide.createDocument(options.node);

            var extraOptions = this.openEditor(options.doc, options.init, options.active, options.forceOpen);
            ide.dispatchEvent("openfile", apf.extend(options, extraOptions));
        }
        else
            tabs.set(path);
    },

    enable : function(){
//        this.hbox.show();
        //this.splitter.show();
    },

    disable : function(){
//        this.hbox.hide();
        //this.splitter.hide();
    },

    destroy : function(){
        menus.remove("View/Tab Bar");
        menus.remove("View/Editors/");

//        this.hbox.destroy(true, true);
        //this.splitter.destroy(true, true);
    }
});

});
Example #8
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");

module.exports = ext.register("ext/recentfiles/recentfiles", {
    dev         : "Ajax.org",
    name        : "Recent Files",
    alone       : true,
    type        : ext.GENERAL,
    deps        : [],
    offline     : false,

    currentSettings : [],
    nodes       : [],

    init : function(){
        var _self = this;

        this.nodes.push(
            ide.mnuFile.insertBefore(new apf.item({
                caption : "Open Recent",
                submenu : "mnuRecent"
            }), ide.mnuFile.firstChild),

            apf.document.documentElement.appendChild(this.menu = new apf.menu({
                id : "mnuRecent",
                childNodes : [
                    this.divider = new apf.divider(),
                    new apf.item({
                        caption : "Clear Menu",
                        onclick : function(){
                            _self.clearMenu();
                        }
                    })
                ]
            }))
        );

        ide.addEventListener("loadsettings", function(e){
            var model = e.model;
            var strSettings = model.queryValue("auto/recentfiles");
            if (strSettings) {
                var currentSettings;
                try {
                    currentSettings = JSON.parse(strSettings);
                }
                catch (ex) {
                    //fail! revert to default
                    currentSettings = [];
                }

                _self.clearMenu();

                for (var i = currentSettings.length - 1; i >= 0; i--) {
                    _self.$add(currentSettings[i]);
                }
            }
        });

        ide.addEventListener("savesettings", function(e){
            if (!_self.changed)
                return;

            var xmlSettings = apf.createNodeFromXpath(e.model.data, "auto/recentfiles/text()");

            var currentSettings = [];
            var nodes = _self.menu.childNodes;
            for (var i = 0; i < nodes.length; i++) {
                if (nodes[i].localName == "item") {
                    currentSettings.push({
                        caption : nodes[i].caption,
                        value   : nodes[i].value
                    });
                }
                else break;
            }

            xmlSettings.nodeValue = JSON.stringify(currentSettings);
            return true;
        });

        function evHandler(e){
            var node = e.node || e.xmlNode;

            if (!node)
                return;

            if (e.name != "afterfilesave" && node.getAttribute("newfile") == 1)
                return;

            var obj = {
                caption : node.getAttribute("name"),
                value   : node.getAttribute("path"),
                node    : node
            };

            _self.currentSettings.shift(obj);

            _self.$add(obj);
        }

        ide.addEventListener("afteropenfile", evHandler);
        ide.addEventListener("afterfilesave", evHandler);
        ide.addEventListener("closefile", evHandler);
    },

    $add : function(def) {
        var found, nodes = this.menu.childNodes;
        for (var i = 0; i < nodes.length; i++) {
            if (nodes[i].nodeType != 1) continue;

            if (nodes[i].localName == "item") {
                if (nodes[i].value == def.value) {
                    found = nodes[i];
                    break;
                }
            }
            else break;
        }

        if (found) {
            this.menu.insertBefore(found, this.menu.firstChild);
        }
        else {
            this.menu.insertBefore(new apf.item({
                caption : def.caption,
                value   : def.value,
                onclick : function(){
                    var node = apf.getXml("<file />");
                    node.setAttribute("name", def.caption);
                    node.setAttribute("path", def.value);

                    ide.dispatchEvent("openfile", {doc: ide.createDocument(node)});
                }
            }), this.menu.firstChild);
        }

        while (this.menu.childNodes.length > 12) {
            this.menu.removeChild(this.divider.previousSibling);
        }

        this.changed = true;
    },

    clearMenu : function(){
        var nodes = this.menu.childNodes;
        for (var i = nodes.length - 1; i >= 0; i--) {
            if (nodes[0].localName == "item")
                this.menu.removeChild(nodes[0]);
            else break;
        }
    },

    enable : function(){
        this.nodes.each(function(item){
            item.enable();
        });
    },

    disable : function(){
        this.nodes.each(function(item){
            item.disable();
        });
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
    }
});

});
Example #9
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var css = require("text!ext/uploadfiles/uploadfiles.css");
var skin = require("text!ext/uploadfiles/skin.xml");
var markup = require("text!ext/uploadfiles/uploadfiles.xml");
var fs   = require("ext/filesystem/filesystem");
var menus = require("ext/menus/menus");

var MAX_UPLOAD_SIZE_FILE = 52428800;
var MAX_OPENFILE_SIZE = 2097152;
var MAX_CONCURRENT_FILES = 1000;

module.exports = ext.register("ext/uploadfiles/uploadfiles", {
    dev         : "Ajax.org",
    name        : "Upload Files",
    alone       : true,
    skin    : {
        id  : "uploadfiles",
        data : skin,
        "media-path" : ide.staticPrefix + "/ext/uploadfiles/style/images/"
    },
    type        : ext.GENERAL,
    css         : css,
    markup      : markup,
    deps        : [],
    offline     : false,
    worker      : null,
    
    currentSettings : [],
    nodes       : [],
    
    uploadQueue : [], // list of all files that are queued for upload
    lockHideQueueItems: [], // list of completed downloads that still needs to be removed from the upload queue
    
    hook : function(){
        var _self = this;
        ide.addEventListener("init.ext/tree/tree", function(){
            _self.nodes.push(
                menus.addItemByPath("File/~", new apf.divider(), 350),
                menus.addItemByPath("File/Upload Files", new apf.item({
                    onclick : function(){
                        ext.initExtension(_self);
                        winUploadFiles.show();
                    }
                }), 370)
            );
            
            mnuCtxTree.addEventListener("afterrender", function(){
                _self.nodes.push(
                    mnuCtxTree.insertBefore(new apf.item({
                        id : "mnuCtxTreeUpload",
                        match : "[folder]",
                        visible : "{trFiles.selected.getAttribute('type')=='folder'}",
                        caption : "Upload to this folder",
                        onclick : function(){
                            ext.initExtension(_self);
                            
                            winUploadFiles.show();
                        }
                    }), itemCtxTreeNewFile),
                    mnuCtxTree.insertBefore(new apf.divider({
                        visible : "{mnuCtxTreeUpload.visible}"
                    }), itemCtxTreeNewFile)
                )
            });
            
            if(ide.infraEnv) {
                _self.nodes.push(
                    menus.addItemByPath("File/Download Project", new apf.item({
                        onclick : function(){
                            window.open("/api/project/download/zip/" + ide.projectName);
                        }
                    }), 390)
                );
                btnUploadFiles.setProperty("right", "81");
            }
        });
    },
    
    init : function(){
        var _self = this;
        apf.importCssString(_self.css);
        
        // disabled download project since it doesn't work anymore due to runvm changes
        
        winUploadFiles.addEventListener("afterrender", function(){
            this.filebrowser = fileUploadSelect.$ext;
            this.filebrowser.addEventListener('change', handleFileSelect, false);
            
            // enable webkit folder upload
            if (apf.isWebkit) {
                hboxUploadNoFolders.hide();
                hboxUploadWithFolders.show();
                
                apf.setStyleClass(fileUploadSelectBtn.$ext, "uploadWithFolders")
                
                this.folderbrowser = folderUploadSelect.$ext;
                this.folderbrowser.style.display = "block";
                this.folderbrowser.addEventListener('change', handleFileSelect, false);
            }
        });
        
        function handleFileSelect(e){
            var files = e.target.files;
            
            if (!(_self.checkUploadSize(files) && _self.checkNumberOfFiles(files)))
                return false;
        
            _self.startUpload(files);
        };
        
        ide.addEventListener("init.ext/tree/tree", function(){
            /*
            winFilesViewer.appendChild(new apf.vbox({
                id : "vboxTreeContainer",
                anchors: "0 0 0 0"
            }));
        
            vboxTreeContainer.appendChild(trFiles);
            */
            vboxTreeContainer.appendChild(boxUploadActivity);
        });
        
        
        lstUploadActivity.$ext.addEventListener("mouseover", function(e) {
            _self.lockHideQueue = true;
            if (!apf.isChildOf(this, e.relatedTarget)) {
                _self.lockHideQueue = true;
            }
        });
        
        lstUploadActivity.$ext.addEventListener("mouseout", function(e) {
            if (apf.isChildOf(this, e.relatedTarget))
                return;
                
            _self.lockHideQueue = false;
            _self.clearCompletedUploads();
        });
    },
    
    initWorker: function() {
        var _self = this;
        
        this.worker = new Worker('/static/ext/uploadfiles/uploadworker.js');
        this.worker.onmessage = function(e) {  
            var data = e.data;
            if (!data.type) {
                //console.log(data);
                return;
            }
            switch(data.type) {
                case "complete":
                    _self.onComplete();
                    break;
                case "progress":
                    _self.onProgress(data.value);
                    break;
                case "paused":
                    ide.addEventListener("afteronline", function(e) {
                        // upload current file again
                        _self.upload();
                    });
                    break;
                case "debug":
                    console.log(JSON.stringify(data));
                default:
                    console.log("unknown message from uploadworker: ", data.type);
            }
        };  
    },
    
    onShow : function(){
        if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
            alert('The File APIs are not fully supported in this browser.');
            return winUploadFiles.hide();
        }
        
        this.setTargetFolder();
        
        // disable tabEditors dropzone for upload
        tabEditors.$ext.disableDropbox = true;
        
        trFiles.addEventListener("afterselect", this.setTargetFolder);
    },
    
    onClose : function() {
        tabEditors.$ext.disableDropbox = false;
        trFiles.removeEventListener("afterselect", this.setTargetFolder);
    },
    
    getTargetFolder : function(){
        var target = trFiles.selected 
            ? trFiles.selected.nodeName == "file"
                ? trFiles.selected.parentElement
                : trFiles.selected
            : trFiles.root.firstChild;
        
        return target;
    },
    
    setTargetFolder: function() {
        var targetfolder = require("ext/uploadfiles/uploadfiles").getTargetFolder();
        if (!targetfolder)
            trFiles.root.firstChild;
            
        uplTargetFolder.$ext.innerHTML = targetfolder.getAttribute("name");
    },
    
    /* upload functionality */
    onBeforeDrop: function(e) {
        if (!(window.File && window.FileReader/* && window.FormData*/)) {
            util.alert(
                "Could not upload file(s)", "An error occurred while dropping this file(s)",
                "Your browser does not offer support for drag and drop for file uploads. " +
                "Please try with a recent version of Chrome or Firefox."
            );
            return false;
        }
        
        /** Dropped item is a folder */
        if (e.dataTransfer.files.length == 0) {
            ext.initExtension(this);
            
            winNoFolderSupport.show();
            
            if (!apf.isWebkit)
                btnNoFolderSupportOpenDialog.hide();
            
            return false;
        }
        var files = e.dataTransfer.files;
        if (!(this.checkUploadSize(files) && this.checkNumberOfFiles(files)))
            return false;
        
        if (e.dataTransfer.files.length < 1)
            return false;
        
        this.onDrop(e);
        
        return true;
    },
    
    onDrop: function(e) {
        ext.initExtension(this);
        
        var dt = e.dataTransfer;
        var files = dt.files;

        this.startUpload(files);
    },

    startUpload: function(files) {
        var _self = this;
        this.numFilesUploaded = files.length;
        
        var node = trFiles.selected;
        if (!node)
            node = trFiles.xmlRoot.selectSingleNode("folder");
            
        if (node.getAttribute("type") != "folder" && node.tagName != "folder")
            node = node.parentNode;
        
        // hide upload window if visible
        if (winUploadFiles.visible)
            winUploadFiles.hide();
            
        // show upload activity list
        boxUploadActivity.show();
        
        // set hidden files to true to support hidden files and folders
        (davProject.realWebdav || davProject).setAttribute("showhidden", true);
        
        // loop through all files to create the upload queue and create subfolders
        apf.asyncForEach(files, function(file, next) {
            file.targetFolder = node;
            
            var folders = file.webkitRelativePath && file.webkitRelativePath.split("/");
            
            if (!folders || !folders.length) {
                addFile(file);
                return next();
            }
            
            // a folder is uploaded
            else {
                //davProject.setProperty("showhidden", true);
                folders.pop(); // remove filename from path
                
                var targetPath = folders.join("/");
                var nodePath = node.getAttribute("path");
                var targetFolder = trFiles.getModel().data.selectSingleNode("//folder[@path='" + apf.escapeXML(nodePath) + "/" + apf.escapeXML(targetPath) + "']");
                
                // check if folder with specified path already exists
                if (targetFolder) {
                    // this is a folder, no need to add to upload queue
                    if (file.name == ".")
                        return next();

                    addFile(file, targetFolder);
                    return next();
                }
                // target folder not created yet, create first
                else {
                    var subfolder;
                    var currentPath = nodePath;
                    apf.asyncForEach(folders, function(folder, next2) {
                        currentPath += "/" + folder;
                        subfolder = trFiles.getModel().data.selectSingleNode("//folder[@path='" + apf.escapeXML(currentPath) + "']");
                        
                        // subfolder is already created
                        if (subfolder) {
                            trFiles.select(subfolder);
                            next2();
                        }
                        // subfolder does not exists, create first
                        else {
                            fs.createFolder(folder, trFiles, true, function(folder) {
                                if (!folder) {
                                    _self.removeFromQueue(file.name);
                                    next();
                                }
                                // check if there are subfolders to be created
                                trFiles.select(folder);
                                subfolder = folder;
                                next2();
                            });
                        }
                    }, function() {
                        // this is a folder, no need to add to upload queue
                        if (file.name == ".")
                            return next();
                            
                        addFile(file, subfolder);
                        next();
                    });
                }
            }
        }, function() {
            if (!_self.uploadInProgress)
                _self.uploadNextFile();
        });
        
        // add a file to the upload queue 
        function addFile(file, targetFolder) {
            if (targetFolder)
                file.targetFolder = targetFolder;
            _self.addToQueue(file);
        }
    },
    
    checkSelectableFile: function(event) {
        if (event.selected && event.selected.getAttribute("type") == "fileupload") 
            return false;
    },
    
    addToQueue: function(file) {
        // add files in dirty state
        var parent = file.targetFolder;
        var path = parent.getAttribute("path");
        
        // expand target folder in tree
        trFiles.slideOpen(null, parent, true);
        
        // add node to file tree
        var xmlNode = "<file type='fileupload'" +
            " name='" + apf.escapeXML(file.name) + "'" +
            " path='" + apf.escapeXML(path) + "/" + apf.escapeXML(file.name) + "'" +
        "/>";
        
        file.treeNode = trFiles.add(xmlNode, parent);
        file.path = path;
        // add file to upload activity list
        var queueNode = '<file name="' + apf.escapeXML(file.name) + '" />';
        
        file.queueNode = mdlUploadActivity.appendXml(queueNode);
        
        this.uploadQueue.push(file);
    },
    
    removeFromQueue: function(name) {
        var file;
        for (var i = 0, l = this.uploadQueue.length; i < l; i++) {
            file = this.uploadQueue[i];
            if (file.name == name) {
                this.uploadQueue.splice(i, 1);
                this.removeCurrentUploadFile(name);
                break;
            }
        }
    },
    
    removeCurrentUploadFile: function(name) {
        var file = this.currentFile;
        apf.xmldb.removeNode(file.queueNode);
        apf.xmldb.removeNode(file.treeNode);
        if (!mdlUploadActivity.data.childNodes.length) {
            boxUploadActivity.hide();
        }
    },
    
    // remove queued items from upload activity list that are completed uploading but were not removed yet
    // because user had his mousecursor on the list.
    clearCompletedUploads: function() {
        var _self = this;
        var completedUploads = mdlUploadActivity.queryNodes("file[@progress='100']");
        apf.asyncForEach(completedUploads, function(item, next) {
            if (_self.lockHideQueue)
                return;
            setTimeout(function() {    
                apf.xmldb.removeNode(item);
                next();
            }, 200);
        }, function() {
            
        });
    },
    
    uploadNextFile: function() {
        var _self = this;

        var file = this.currentFile = this.uploadQueue.shift();
        
        // check if there is a file to upload
        if (file) {
            this.uploadInProgress = true;
            if (this.hideUploadActivityTimeout) {
                clearTimeout(this.hideUploadActivityTimeout);
                this.hideUploadActivityTimeout = null;
            }
            
            /** Chrome, Firefox */
            if (apf.hasFileApi) {
                function checkFileExists(exists) {
                    if (exists) {
                        if (_self.existingOverwriteAll) {
                            _self.overwrite();
                        }
                        else if (_self.existingSkipAll) {
                            _self.removeCurrentUploadFile(file.name);
                            _self.uploadNextFile();
                        }
                        else {
                            winUploadFileExists.show();
                            if (_self.uploadQueue.length) {
                                btnUploadOverwriteAll.show();
                                btnUploadSkipAll.show();
                            }
                            else {
                                btnUploadOverwriteAll.hide();
                                btnUploadSkipAll.hide();
                            }
                            uploadFileExistsMsg.$ext.innerHTML = "\"" + apf.escapeXML(file.name) + "\" already exists, do you want to replace it? Replacing it will overwrite its current contents.";
                        }
                    }
                    else {
                        upload(file);
                    }
                }
                
                function upload(file) {
                    var file = file || _self.currentFile;
                    var node = file.queueNode;
                    apf.xmldb.setAttribute(node, "progress", 0);
                    
                    if (!_self.worker)
                        _self.initWorker();
                    _self.worker.postMessage({cmd: 'connect', id: file.name, file: file, path: file.path});
                }
                _self.upload = upload;
                
                fs.exists(file.path + "/" + file.name, checkFileExists);
            }
        }
        // no files in queue, upload completed
        else {
            _self.uploadInProgress = false;
            _self.existingOverwriteAll = false;
            _self.existingSkipAll = false;
            (davProject.realWebdav || davProject).setAttribute("showhidden", require('ext/settings/settings').model.queryValue("auto/projecttree/@showhidden"));
            this.hideUploadActivityTimeout = setTimeout(function() {
                mdlUploadActivity.load("<data />");
                boxUploadActivity.hide();
            }, 5000);
        }
    },
    
    /** Check for files exceeding filesize limit */
    checkUploadSize: function(files) {
        var file;
        var files_too_big = [];
        for (var filesize, totalsize = 0, i = 0, l = files.length; i < l; ++i) {
            file = files[i];
            filesize = file.size;
            totalsize += filesize;

            if (filesize > MAX_UPLOAD_SIZE_FILE) {
                files_too_big.push(file.name)
            }
        }
        
        if (files_too_big.length) {
            if (files_too_big.length == 1) {
                util.alert(
                    "Maximum file-size exceeded", "A file exceeds our upload limit of 50MB per file.",
                    "Please remove the file '" + files_too_big[0] + "' from the list to continue."
                );
            }
            else {
                util.alert(
                    "Maximum file-size exceeded", "Some files exceed our upload limit of 50MB per file.",
                    "Please remove any files larger than 50MB from the list to continue."
                );
            }
            
            return false;
        }
        
        return true;
    },
    
    /** Check the number of dropped files exceeds the limit */
    checkNumberOfFiles: function(files) {
        if (files.length > MAX_CONCURRENT_FILES) {
            util.alert(
                "Could not upload files", "An error occurred while dropping these files",
                "You can only drop " + MAX_CONCURRENT_FILES + " files to upload at the same time. " + 
                "Please try again with " + MAX_CONCURRENT_FILES + " or fewer files."
            );
            
            return false;
        }
        
        return true;
    },
    
    skip: function() {
        this.removeCurrentUploadFile(this.currentFile.name);
        this.uploadNextFile();
    },
    
    skipAll: function() {
        this.existingSkipAll = true;
        this.skip();
    },
    
    overwrite: function() {
        var file = this.currentFile;
        var node = file.targetFolder;
        var path     = node.getAttribute("path");
        var filename = file.name;
        
        apf.xmldb.removeNode(file.treeNode);
        fs.remove(path + "/" + filename, this.upload);
    },
    
    overwriteAll: function() {
        this.existingOverwriteAll = true;
        this.overwrite();
    },
    
    onProgress: function(perc) {
        if(!this.currentFile) return;    
        var total = Math.floor(perc * 100);
        var node = this.currentFile.queueNode;
        var curPerc = node.getAttribute("progress")
        apf.xmldb.setAttribute(node, "progress", Math.max(total, curPerc));
    },
    
    onComplete: function() {
        var _self = this;
        var file = this.currentFile;
        var path = file.path;
        
        apf.xmldb.setAttribute(file.queueNode, "progress", "100");
        fs.webdav.exec("readdir", [path], function(data) {
            if (data instanceof Error) {
                // @todo: in case of error, show nice alert dialog.
                return _self.uploadNextFile();
            }
            
            var strXml = data.match(new RegExp(("(<file path='" + apf.escapeXML(path) +
                "/" + apf.escapeXML(file.name) + "'.*?>)").replace(/\//g, "\\/"))) ||
                data.match(new RegExp(('(<file path="' + apf.escapeXML(path) +
                "/" + apf.escapeXML(file.name) + '".*?>)').replace(/\//g, "\\/")));;
            
            if(!strXml) {
                _self.uploadNextFile();
            }

            // change file from uploading to file to regular file in tree
            apf.xmldb.setAttribute(file.treeNode, "type", "file");
            
            // remove file from upload activity lilst
            setTimeout(function() {
                if (!_self.lockHideQueue)
                    apf.xmldb.removeNode(file.queueNode);
                else
                    _self.lockHideQueueItems.push(file);
            }, 2000);
            
            _self.uploadNextFile();
        });
    },
    
    getFormData: function(file) {
        var form = new FormData();
        form.append("upload", file);
        
        return form;
    },
    
    
    enable : function(){
        this.nodes.each(function(item){
            item.enable();
        });
    },

    disable : function(){
        this.nodes.each(function(item){
            item.disable();
        });
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
    }
});

});
Example #10
0
define(function(require, exports, module) {

var ext = require("core/ext");
var ide = require("core/ide");
var settings = require("core/settings");
var panels = require("ext/panels/panels");
var editors = require("ext/editors/editors");
var anims = require("ext/anims/anims");

var shadowOpen = "0px 1px 0px rgba(255, 255, 255, 0.05) inset, "
    + "-1px 0px 0px 0px black inset, "
    + "1px 0px 0px 0px rgba(255, 255, 255, 0.06)";

var shadowClosed = "0px 1px 0px rgba(255, 255, 255, 0.05) inset";

module.exports = ext.register("ext/sidebar/sidebar", {
    name     : "Side Bar",
    dev      : "Cloud9 IDE, Inc.",
    alone    : true,
    type     : ext.GENERAL,

    nodes : [],

    init : function(){
        var _self = this;

        function btnClick(){
            if (panels.currentPanel)
                panels.deactivate(null, true);
            else if (panels.lastPanel)
                panels.activate(panels.lastPanel);
            else {
                navbar.childNodes[1].dispatchEvent("mousedown")
                navbar.childNodes[1].setValue(true);
            }
        }
        
        this.nodes.push(
            hboxTabBar.insertBefore(new apf.hbox({
                id: "navbar",
                "class": "black-menu-bar",
                style: "background:rgba(24,24,24,.9) url(" + ide.staticPrefix 
                    + "/ext/main/style/images/c9-noise.png);box-shadow: " 
                    + shadowOpen,
                "minwidth": "45",
                childNodes : [
                    new apf.button({
                        skin    : "mnubtn",
                        "class" : "c9-logo",
                        onclick : btnClick
                    }),
                    this.btnArrow = new apf.button({
                        skin    : "mnubtn",
                        visible : "false",
                        "class" : "toggle-black-menu-bar",
                        onclick : btnClick
                    })
                ]
            }), hboxTabBar.firstChild)
        );
    
        var timer, closed;
        var panelAnimate = function(e, callback){
            //Stop and prevent any animation to happen
            clearTimeout(timer);
            _self.animating = true;

            closed = e.toWidth ? false : true;

            var l = navbar.$int.lastChild.previousSibling;
            var w = l.offsetLeft + l.offsetWidth + (_self.btnArrow.visible ? 1 : 6);
            
            setTimeout(function(){
                if (!e.toWidth) {
                    apf.setStyleClass(navbar.$int, "closed");
                    navbar.$int.style.boxShadow = shadowClosed;
                    _self.btnArrow.show();
                    if (panels.lastPanel)
                        panels.lastPanel.button.$setState("Out", {});
                }
                else {
                    apf.setStyleClass(navbar.$int, "", ["closed"]);
                    navbar.$int.style.boxShadow = shadowOpen;
                    _self.btnArrow.hide();
                }
            }, e.noanim ? 0 : 50);

            anims.animateSplitBoxNode(navbar, {
                immediate: e.noanim,
                width: (e.toWidth 
                    ? (Math.max(parseInt(e.toWidth), w)) 
                    : (editors.showTabs ? 45 : 9)) + "px", 
                timingFunction: "cubic-bezier(.10, .10, .25, .90)", 
                duration: 0.15
            }, function(){
                callback && callback();
                _self.animating = false;
            });
        };
        
        ide.addEventListener("panels.animate", panelAnimate);

        splitterPanelLeft.addEventListener("dragmove", function(e){
            _self.setExpandedSize();
        });
        splitterPanelLeft.addEventListener("dragdrop", function(e){
            _self.setExpandedSize();
        });
        
        var toggleTabs =  function(e){
            var setMinWidth = function(){
                navbar.setAttribute("minwidth", !e.value ? 0 : 45);
            }
            
            if (closed) {
                if (!e.value) {
                    navbar.setAttribute("minwidth", 0);
                    apf.setStyleClass(navbar.$int, "minimized");
                }
                else {
                    apf.setStyleClass(navbar.$int, "", ["minimized"]);
                    navbar.setWidth(8);
                }
                panelAnimate(e, setMinWidth);
            }
            else
                setMinWidth();
        };
        
        ide.addEventListener("tabs.visible", toggleTabs);
        
        ide.addEventListener("settings.load", function(e){
            var activePanel = e.model.queryValue("auto/panels/@active");
            if (activePanel == "none") {
                panelAnimate({noanim: true});
            } else {
                ide.addEventListener("init." + activePanel, function(e){
                    if (e.ext.button)
                        e.ext.button.setValue(true);
                    
                    if (colLeft.visible) {
                        _self.setExpandedSize();
                    }
                    else {
                        colLeft.addEventListener("prop.visible", function(){
                            _self.setExpandedSize();
                            
                            colLeft.removeEventListener("prop.visible", arguments.callee);
                        });
                    }
                });
            }
            
            var showTabs = e.model.queryValue("auto/tabs/@show");
            toggleTabs({value: apf.isTrue(showTabs)});
            
            _self.settingsLoaded = true;
        });
    },
    
    animateToFullWidth : function(){
        editors.pauseTabResize();
        
        var toWidth = navbar.$int.scrollWidth + (editors.showTabs ? 6 : 9);
        anims.animateSplitBoxNode(navbar, {
            width: toWidth + "px", 
            timingFunction: "cubic-bezier(.10, .10, .25, .90)", 
            duration: 0.3 
        }, function(){
            apf.layout.forceResize();
        });
    },
    
    animateToDefaultWidth : function(immediate){
        var toWidth = colLeft.getWidth();
        anims.animateSplitBoxNode(navbar, {
            width: toWidth + "px", 
            timingFunction: "cubic-bezier(.10, .10, .25, .90)", 
            duration: 0.3,
            immediate: immediate
        }, function(){
            apf.setStyleClass(navbar.$int, "closed");
            apf.layout.forceResize();
            editors.continueTabResize();
        });
    },
    
    setExpandedSize : function (){
        var l = navbar.$int.lastChild.previousSibling;
        var w = l.offsetLeft + l.offsetWidth + (this.btnArrow.visible ? 6 : 1);
        navbar.setWidth(Math.max(w, colLeft.getWidth()));
    },
    
    add : function(panelExt, options) {
        var beforePanel, diff = 1000000;
        for (var path in panels.panels) {
            var d = panels.panels[path].$panelPosition - options.position;
            if (d > 0 && d < diff && panels.panels[path].button) {
                beforePanel = panels.panels[path];
                diff = d;
            }
        }
        
        panelExt.button = navbar.insertBefore(new apf.button({
            skin    : "mnubtn",
            state   : "true",
            "class" : options["class"],
            caption : options.caption
        }), beforePanel && beforePanel.button || this.btnArrow);

        panelExt.button.addEventListener("mouseover", function(e){
            if (panels.currentPanel)
                panels.currentPanel.panel.setTitle(this.caption);
        });
        
        panelExt.button.addEventListener("mouseout", function(e){
            if (panels.currentPanel)
                panels.currentPanel.panel.setTitle(
                    panels.currentPanel.button.caption);
        });

        panelExt.button.addEventListener("mousedown", function(e){
            var value = this.value;
            if (panels.currentPanel && (panels.currentPanel != panelExt || value) && value) {
                //panels.currentPanel == panelExt
                panels.deactivate(false, true);
                
                if (value) {
                    if (!apf.isTrue(settings.model.queryValue('general/@animateui')))
                        colLeft.hide();
                    return;
                }
            }
            
            panels.activate(panelExt, true);
        });
        
        if (this.settingsLoaded 
          && settings.model.queryValue("auto/panels/@active") != "none") {
            this.setExpandedSize();
        }
        
        panelExt.nodes.push(panelExt.button, panelExt.mnuItem);
    }
});

});
Example #11
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var fs = require("ext/filesystem/filesystem");
var css = require("text!ext/save/save.css");
var menus = require("ext/menus/menus");
var markup = require("text!ext/save/save.xml");

module.exports = ext.register("ext/save/save", {
    dev         : "Ajax.org",
    name        : "Save",
    alone       : true,
    type        : ext.GENERAL,
    markup      : markup,
    css         : css,
    deps        : [fs],
    offline     : true,

    commands    : {
        "quicksave": {hint: "save the currently active file to disk"},
        "saveas": {hint: "save the file to disk with a different filename"},
        "reverttosaved": {hint: "downgrade the currently active file to the last saved version"}
    },
    hotitems    : {},
    nodes       : [],
    saveBuffer  : {},

    hook : function(){
        var _self = this;

        ide.addEventListener("init.ext/editors/editors", function(){
            tabEditors.addEventListener("close", _self.$close = function(e) {
                var at = e.page.$at;
                if (!at.undo_ptr)
                    at.undo_ptr = at.$undostack[0];
                var node = e.page.$doc.getNode();
                if (node && at.undo_ptr && at.$undostack[at.$undostack.length-1] !== at.undo_ptr
                  || !at.undo_ptr && node.getAttribute("changed") == 1
                  && e.page.$doc.getValue()) {

                    ext.initExtension(_self);
    
                    if (apf.isTrue(require("ext/settings/settings").model.queryValue("general/@autosaveenabled"))) {
                        return;
                    }

                    var pages   = tabEditors.getPages(),
                    currIdx = pages.indexOf(e.page);
                    tabEditors.set(pages[currIdx].id); //jump to file
    
                    var filename = node.getAttribute("path").replace(ide.workspaceDir, "").replace(ide.davPrefix, "");
    
                    winCloseConfirm.page = e.page;
                    winCloseConfirm.all  = -100;
                    winCloseConfirm.show();
    
                    fileDesc.replaceMarkup("<div><h3>Save " + apf.escapeXML(filename) + "?</h3><div>This file has unsaved changes. Your changes will be lost if you don't save them.</div></div>", {"noLoadingMsg": false});
    
                    winCloseConfirm.addEventListener("hide", function(){
                        if (winCloseConfirm.all != -100) {
                            var f = function(resetUndo){
                                var page;
                                if (!(page=winCloseConfirm.page))
                                    return;
    
                                tabEditors.remove(page, true, page.noAnim);
                                delete page.noAnim;
                                if (resetUndo)
                                    page.$at.undo(-1);
                                delete winCloseConfirm.page;
                                page.dispatchEvent("aftersavedialogclosed");
                            };
    
                            if (winCloseConfirm.all == -200)
                                _self.quicksave(winCloseConfirm.page, f);
                            else
                                f(true);
                            /*winSaveAs.page = winCloseConfirm.page;*/
                        }
                        else
                            tabEditors.dispatchEvent("aftersavedialogcancel");
    
                        winCloseConfirm.removeEventListener("hide", arguments.callee);
                    });
    
                    btnYesAll.hide();
                    btnNoAll.hide();
    
                    e.preventDefault();
                }
            });
        });

        this.nodes.push(
            menus.$insertByIndex(barTools, new apf.button({
                id       : "btnSave",
                icon     : "save.png",
                caption  : "Save",
                tooltip  : "Save",
                skin     : "c9-toolbarbutton",
                disabled : "{!!!tabEditors.activepage}",
                onclick  : this.quicksave.bind(this)
            }), 1000)
        );

        var saveItem, saveAsItem;
        this.nodes.push(
            saveItem = menus.addItemByPath("File/Save", new apf.item({
                onclick : this.quicksave.bind(this),
                disabled : "{!!!tabEditors.activepage}"
            }), 1000),

            saveAsItem = menus.addItemByPath("File/Save As...", new apf.item({
                onclick : function () {
                    _self.saveas();
                },
                disabled : "{!!!tabEditors.activepage}"
            }), 1100),

            menus.addItemByPath("File/Save All", new apf.item({
                onclick : function(){
                    _self.saveall();
                },
                disabled : "{!!!tabEditors.activepage}"
            }), 1200),

            menus.addItemByPath("File/Revert to Saved", new apf.item({
                onclick : function(){
                    _self.reverttosaved();
                },
                disabled : "{!!!tabEditors.activepage}"
            }), 700)
        );

        this.hotitems.quicksave = [saveItem];
        this.hotitems.saveas = [saveAsItem];
    },

    init : function(amlNode){
        this.fileDesc = winCloseConfirm.selectSingleNode("a:vbox");

        apf.importCssString((this.css || ""));
        winCloseConfirm.onafterrender = function(){
            btnYesAll.addEventListener("click", function(){
                winCloseConfirm.all = 1;
                winCloseConfirm.hide();
            });
            btnNoAll.addEventListener("click", function(){
                winCloseConfirm.all = -1;
                winCloseConfirm.hide();
            });
            btnSaveYes.addEventListener("click", function(){
                winCloseConfirm.all = -200;
                winCloseConfirm.hide();
            });
            btnSaveNo.addEventListener("click", function(){
                winCloseConfirm.all = 0;
                winCloseConfirm.hide();
            });
            btnSaveCancel.addEventListener("click", function(){
                winCloseConfirm.all = -100;
                winCloseConfirm.hide();
            });
        };

        winSaveAs.addEventListener("hide", function(){
            if (winSaveAs.page) {
                tabEditors.remove(winSaveAs.page, true);
                winSaveAs.page.$at.undo(-1);
                delete winSaveAs.page;
            }
        });
    },

    reverttosaved : function(){
        ide.dispatchEvent("reload", {doc : tabEditors.getPage().$doc});
    },

    saveall : function() {
        // previous version of this function used
        // .forEach(this.quicksave), but that also passes the index parameter (2nd one)
        // of forEach to the quicksave function.
        var self = this;
        tabEditors.getPages().forEach(function (page) {
            self.quicksave(page);
        });
    },

    saveAllInteractive : function(pages, callback){
        ext.initExtension(this);

        winCloseConfirm.all = 0;

        var _self = this;
        apf.asyncForEach(pages, function(item, next) {
            var at = item.$at;
            if (at.undo_ptr && at.$undostack[at.$undostack.length-1] !== at.undo_ptr) {
                if (winCloseConfirm.all == 1)
                    _self.quicksave(item);

                if (winCloseConfirm.all)
                    return next();

                tabEditors.set(item);
                winCloseConfirm.page = item;
                winCloseConfirm.show();
                winCloseConfirm.addEventListener("hide", function(){
                    if (winCloseConfirm.all == 1)
                        _self.quicksave(item);

                    winCloseConfirm.removeEventListener("hide", arguments.callee);
                    next();
                });

                btnYesAll.setProperty("visible", pages.length > 1);
                btnNoAll.setProperty("visible", pages.length > 1);
            }
            else
                next();
        },
        function() {
            callback(winCloseConfirm.all);
        });
    },

    // `silentsave` indicates whether the saving of the file is forced by the user or not.
    quicksave : function(page, callback, silentsave) {
        if (!page || !page.$at)
            page = tabEditors.getPage();

        if (!page)
            return;
            
        if (typeof callback !== "function") {
            callback = null;
        }

        var doc  = page.$doc;
        var node = doc.getNode();
        var path = node.getAttribute("path");

        if (node.getAttribute("debug"))
            return;

        if (ide.dispatchEvent("beforefilesave", {node: node, doc: doc }) === false)
            return;

        if (node.getAttribute("newfile")){
            this.saveas(page, callback);
            return;
        }

        if (callback) {
            ide.addEventListener("afterfilesave", function(e) {
                if (e.node == node) {
                    callback();
                    this.removeEventListener("afterfilesave", arguments.callee);
                }
            });
        }

        // check if we're already saving!
        var saving = parseInt(node.getAttribute("saving"), 10);
        if (saving) {
            this.saveBuffer[path] = page;
            return;
        }

        apf.xmldb.setAttribute(node, "saving", "1");

        var _self = this;

        var value = doc.getValue();

        fs.saveFile(path, value, function(data, state, extra){
            if (state != apf.SUCCESS) {
                util.alert(
                    "Could not save document",
                    "An error occurred while saving this document",
                    "Please see if your internet connection is available and try again. "
                        + (state == apf.TIMEOUT
                            ? "The connection timed out."
                            : "The error reported was " + extra.message));
            }

            ide.dispatchEvent("afterfilesave", {
                node: node,
                doc: doc,
                value: value,
                silentsave: silentsave
            });

            ide.dispatchEvent("track_action", {
                type: "save as filetype",
                fileType: node.getAttribute("name").split(".").pop(),
                success: state != apf.SUCCESS ? "false" : "true"
            });

            apf.xmldb.removeAttribute(node, "saving");
            apf.xmldb.removeAttribute(node, "new");
            apf.xmldb.setAttribute(node, "modifieddate", apf.queryValue(extra.data, "//d:getlastmodified"));

            if (_self.saveBuffer[path]) {
                delete _self.saveBuffer[path];
                _self.quicksave(page);
            }
        });

        var at = page.$at
        at.undo_ptr = at.$undostack[at.$undostack.length-1];
        page.$at.dispatchEvent("afterchange");
        return false;
    },

    _saveAsNoUI: function(page, path, newPath) {
        if (!page || !path)
            return;

        newPath = newPath || path;

        var file = page.$model.data;
        var saving = parseInt(file.getAttribute("saving"), 10);

        if (saving) {
            this.saveBuffer[path] = page;
            return;
        }
        apf.xmldb.setAttribute(file, "saving", "1");

        var self = this;
        var value = page.$doc.getValue();
        fs.saveFile(newPath, value, function(value, state, extra) {
            if (state != apf.SUCCESS) {
                util.alert("Could not save document",
                  "An error occurred while saving this document",
                  "Please see if your internet connection is available and try again.");
            }

            var model = page.$model;
            var node = model.getXml();
            var doc = page.$doc;

            if (path !== newPath || parseInt(node.getAttribute("newfile") || 0, 10) === 1) {
                model.load(node);
                file = model.data;
                fs.beforeRename(file, null, newPath, false);
                doc.setNode(file);
            }

            apf.xmldb.removeAttribute(file, "saving");

            if (self.saveBuffer[path]) {
                delete self.saveBuffer[path];
                self._saveAsNoUI(page);
            }

            if (parseInt(file.getAttribute("newfile") || "0", 10) === 1) {
                apf.xmldb.removeAttribute(file, "newfile");
                apf.xmldb.removeAttribute(file, "changed");
                var xpath = newPath.replace(new RegExp("\/" + cloud9config.davPrefix.split("/")[1]), "")
                                    .replace(new RegExp("\/" + file.getAttribute("name")), "")
                                    .replace(/\/([^/]*)/g, "/node()[@name=\"$1\"]")
                                    .replace(/\/node\(\)\[@name="workspace"\]/, "")
                                    .replace(/\//, "");
                if (xpath) {
                    var oNode  = trFiles.queryNode(xpath);
                    if (oNode && !trFiles.queryNode('//node()[@path="' + newPath + '"]'))
                        apf.xmldb.appendChild(oNode, file);
                }
            }

            ide.dispatchEvent("afterfilesave", {
                node: node,
                doc: doc,
                value: value,
                silentsave: false // It is a forced save, comes from UI
            });
        });

        var at = page.$at
        at.undo_ptr = at.$undostack[at.$undostack.length-1];
        page.$at.dispatchEvent("afterchange");
    },

    choosePath : function(path, select) {
        fs.list((path.match(/(.*)\/[^/]*/) || {})[1] || path, function (data, state, extra) {
            if (new RegExp("<folder.*" + path + ".*>").test(data)) {
                path  = path.replace(new RegExp('\/' + cloud9config.davPrefix.split('/')[1]), '')
                            .replace(/\/([^/]*)/g, "/node()[@name=\"$1\"]")
                            .replace(/\/node\(\)\[@name="workspace"\]/, "")
                            .replace(/\//, "");

                trSaveAs.expandList([path], function() {
                    var node = trSaveAs.getModel().data.selectSingleNode(path);
                    trSaveAs.select(node);
                });
            }
        });
    },

    // Function called from the 'Save As' menu dialog, and from the C9 CLI.
    // It saves a file with a different name, involving UI.
    saveas : function(page, callback){
        if (!page || !page.$at)
            page = tabEditors.getPage();

        if (!page)
            return;

        var path = page ? page.$model.data.getAttribute("path") : false;
        if (!path)
            return;

        ext.initExtension(this);

        if (callback) {
            var doc = page.$doc;
            ide.addEventListener("afterfilesave", function(e){
                if (e.doc == doc) {
                    callback();
                    this.removeEventListener("afterfilesave", arguments.callee);
                }
            });
        }

        var fooPath = path.split('/');
        txtSaveAs.setValue(fooPath.pop());
        lblPath.setProperty('caption', fooPath.join('/') + '/');
        winSaveAs.show();
    },

    // Called by the UI 'confirm' button in winSaveAs.
    confirmSaveAs : function(page) {
        page = page || tabEditors.getPage();
        var file = page.$model.data;
        var path = file.getAttribute("path");
        var newPath = lblPath.getProperty('caption') + txtSaveAs.getValue();

        // check if we're already saving!
        var saving = parseInt(file.getAttribute("saving"), 10);
        if (saving) {
            this.saveBuffer[path] = page;
            return;
        }

        //apf.xmldb.setAttribute(file, "saving", "1");

        var self = this;
        var doSave = function() {
            winConfirm.hide();
            winSaveAs.hide();
            self._saveAsNoUI(page, path, newPath);
        };

        if (path !== newPath || parseInt(file.getAttribute("newfile") || 0, 10) === 1) {
            fs.exists(newPath, function (exists) {
                if (exists) {
                    var name = newPath.match(/\/([^/]*)$/)[1];
                    var folder = newPath.match(/\/([^/]*)\/[^/]*$/)[1];

                    util.confirm(
                        "Are you sure?",
                        "\"" + name + "\" already exists, do you want to replace it?",
                        "A file or folder with the same name already exists in the folder "
                        + folder + ". "
                        + "Replacing it will overwrite it's current contents.",
                        doSave);
                }
                else {
                    doSave();
                }
            });
        }
        else {
            doSave();
        }
    },

    expandTree : function(){
        var _self = this;
        setTimeout(function(){
            var tabPage = tabEditors.getPage(),
                path    = tabPage ? tabPage.$model.data.getAttribute('path') : false,
                isNew   = tabPage.$model.data.getAttribute('newfile');
            if(!isNew)
                _self.choosePath(path);
            else
                trSaveAs.slideOpen(null, trSaveAs.getModel().data.selectSingleNode('//folder'));
        });
    },

    enable : function(){
        this.nodes.each(function(item){
            item.enable();
        });
    },

    disable : function(){
        this.nodes.each(function(item){
            item.disable();
        });
    },

    destroy : function(){
        menus.remove("File/~", 1100),
        menus.remove("File/Save All")
        menus.remove("File/Save As...")
        menus.remove("File/Save")
        menus.remove("File/~", 800)
        menus.remove("File/Revert to Saved")
        
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];

        tabEditors.removeEventListener("close", this.$close);
    }
});

});
Example #12
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var css = require("text!ext/uploadfiles/uploadfiles.css");
var skin = require("text!ext/uploadfiles/skin.xml");
var markup = require("text!ext/uploadfiles/uploadfiles.xml");
var fs   = require("ext/filesystem/filesystem");
var menus = require("ext/menus/menus");
var settings = require('ext/settings/settings');

var MAX_UPLOAD_SIZE_FILE = 52428800; // max size accepted for one file
var MAX_OPENFILE_SIZE = 2097152; // max size of file that is openen in the editor on drop
var MAX_VISIBLE_UPLOADS = 20; // max number of files added to upload activity list
var MAX_OPEN_FILES_EDITOR = 5; // max number of files that can be opened in the editor

module.exports = ext.register("ext/uploadfiles/uploadfiles", {
    dev         : "Ajax.org",
    name        : "Upload Files",
    alone       : true,
    skin    : {
        id  : "uploadfiles",
        data : skin,
        "media-path" : ide.staticPrefix + "/ext/uploadfiles/style/images/"
    },
    type        : ext.GENERAL,
    css         : css,
    markup      : markup,
    deps        : [],
    offline     : false,
    worker      : null,
    
    currentSettings : [],
    nodes       : [],
    
    uploadFiles: [], // list of all files that are queued for upload
    uploadQueue : [], // shortlist of files next in queue for upload
    lockHideQueueItems: [], // list of completed downloads that still needs to be removed from the upload queue
    
    ignoreFiles: [".", ".DS_Store"],
    
    hook : function(){
        var _self = this;
        ide.addEventListener("init.ext/tree/tree", function(){
            _self.nodes.push(
                menus.addItemByPath("File/~", new apf.divider(), 350),
                menus.addItemByPath("File/Upload Files", new apf.item({
                    onclick : function(){
                        ext.initExtension(_self);
                        winUploadFiles.show();
                    }
                }), 370)
            );
            
            mnuCtxTree.addEventListener("afterrender", function(){
                _self.nodes.push(
                    mnuCtxTree.insertBefore(new apf.item({
                        id : "mnuCtxTreeUpload",
                        match : "[folder]",
                        visible : "{trFiles.selected.getAttribute('type')=='folder'}",
                        caption : "Upload to this folder",
                        onclick : function(){
                            ext.initExtension(_self);
                            
                            winUploadFiles.show();
                        }
                    }), itemCtxTreeNewFile),
                    mnuCtxTree.insertBefore(new apf.divider({
                        visible : "{mnuCtxTreeUpload.visible}"
                    }), itemCtxTreeNewFile)
                )
            });
            
            if(window.cloud9config.hosted) {
                _self.nodes.push(
                    menus.addItemByPath("File/Download Project", new apf.item({
                        onclick : function(){
                            window.open("/api/project/download/zip/" + ide.projectName);
                        }
                    }), 390)
                );
            }
        });
    },
    
    init : function(){
        var _self = this;
        apf.importCssString(_self.css);
        
        // disabled download project since it doesn't work anymore due to runvm changes
        
        winUploadFiles.addEventListener("afterrender", function(){
            this.filebrowser = fileUploadSelect.$ext;
            this.filebrowser.addEventListener('change', handleFileSelect, false);
            
            // enable webkit folder upload
            if (apf.isWebkit) {
                hboxUploadNoFolders.hide();
                hboxUploadWithFolders.show();
                
                apf.setStyleClass(fileUploadSelectBtn.$ext, "uploadWithFolders")
                
                this.folderbrowser = folderUploadSelect.$ext;
                this.folderbrowser.style.display = "block";
                this.folderbrowser.addEventListener('change', handleFileSelect, false);
            }
        });
        
        function handleFileSelect(e){
            var files = e.target.files;
            _self.startUpload(files);
        };
        
        ide.addEventListener("init.ext/tree/tree", function(){
            vboxTreeContainer.appendChild(boxUploadActivity);
        });
        
        
        lstUploadActivity.$ext.addEventListener("mouseover", function(e) {
            _self.lockHideQueue = true;
            if (!apf.isChildOf(this, e.relatedTarget)) {
                _self.lockHideQueue = true;
            }
        });
        
        lstUploadActivity.$ext.addEventListener("mouseout", function(e) {
            if (apf.isChildOf(this, e.relatedTarget))
                return;
                
            _self.lockHideQueue = false;
            _self.clearCompletedUploads();
        });
        
        cbToggleUploadQueue.addEventListener("click", function(e) {
            if (!e.currentTarget.checked)
                lstUploadActivity.hide();
            else
                lstUploadActivity.show();
        });
    },
    
    initWorker: function() {
        var _self = this;
        
        this.worker = new Worker('/static/ext/uploadfiles/uploadworker.js');
        this.worker.onmessage = function(e) {  
            var data = e.data;
            if (!data.type) {
                //console.log(data);
                return;
            }
            switch(data.type) {
                case "complete":
                    _self.onComplete();
                    break;
                case "progress":
                    _self.onProgress(data.value);
                    break;
                case "paused":
                    ide.addEventListener("afteronline", function(e) {
                        // upload current file again
                        _self.upload();
                    });
                    break;
                case "debug":
                    console.log(JSON.stringify(data));
                    break;
                case "canceled":
                    this.cancelAllUploads = false;
                    break;
                default:
                    console.log("unknown message from uploadworker: ", data.type);
            }
        };  
    },
    
    onShow : function(){
        if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
            alert('The File APIs are not fully supported in this browser.');
            return winUploadFiles.hide();
        }
        
        this.setTargetFolder();
        
        // disable tabEditors dropzone for upload
        tabEditors.$ext.disableDropbox = true;
        
        trFiles.addEventListener("afterselect", this.setTargetFolder);
    },
    
    onClose : function() {
        tabEditors.$ext.disableDropbox = false;
        trFiles.removeEventListener("afterselect", this.setTargetFolder);
    },
    
    getTargetFolder : function(){
        var target = trFiles.selected 
            ? trFiles.selected.nodeName == "file"
                ? trFiles.selected.parentElement
                : trFiles.selected
            : trFiles.root.firstChild;
        
        return target;
    },
    
    setTargetFolder: function() {
        var targetfolder = require("ext/uploadfiles/uploadfiles").getTargetFolder();
        if (!targetfolder)
            trFiles.root.firstChild;
            
        uplTargetFolder.$ext.innerHTML = targetfolder.getAttribute("name");
    },
    
    /* upload functionality */
    onBeforeDrop: function(e) {
        if (!(window.File && window.FileReader)) {
            util.alert(
                "Could not upload file(s)", "An error occurred while dropping this file(s)",
                "Your browser does not offer support for drag and drop for file uploads. " +
                "Please try with a recent version of Chrome or Firefox."
            );
            return false;
        }
        var files = e.dataTransfer.files;
        // Dropped item is a folder, second condition is for FireFox
        if (!files.length || !files[0].size) {
            ext.initExtension(this);
            
            winNoFolderSupport.show();
            
            // only in Chrome display button to upload dialog with select folder 
            if (!apf.isWebkit)
                btnNoFolderSupportOpenDialog.hide();
            
            return false;
        }
        
        var filesLength = files.length;
        if (filesLength < 1)
            return false;
        
        // if dropped on editor open file
        if (e.currentTarget.id == "tabEditorsDropArea") {
            if (filesLength <= MAX_OPEN_FILES_EDITOR)
                this.openOnUpload = true;
            else {
                return util.alert(
                    "Maximum files exceeded", "The number of files dropped in the editor exceeds the limit.",
                    "Please update your selection to " + MAX_OPEN_FILES_EDITOR + " files to continue."
                );
            }
        }
        
        this.onDrop(e);
        
        return true;
    },
    
    onDrop: function(e) {
        ext.initExtension(this);
        
        this.startUpload(e.dataTransfer.files);
    },

    startUpload: function(files) {
        this.numFilesUploaded = files.length;
        
        var node = trFiles.selected;
        if (!node)
            node = trFiles.xmlRoot.selectSingleNode("folder");
            
        if (node.getAttribute("type") != "folder" && node.tagName != "folder")
            node = node.parentNode;
        
        // hide upload window if visible
        if (winUploadFiles.visible)
            winUploadFiles.hide();
            
        // set hidden files to true to support hidden files and folders
        (davProject.realWebdav || davProject).setAttribute("showhidden", true);
        
        var filesToAddToQueue = [];
        var files_too_big = [];
        var filename;
        this.uploadIndex = -1;
        if (!this.totalNumUploads)
            this.totalNumUploads = 0;
        var fileIndex = -1;
        var filesInQueue = this.uploadInProgress ? (mdlUploadActivity.data && mdlUploadActivity.data.childNodes.length) : 0;

        var file;
        for (var i = 0, l = files.length; i < l; i++) {
            file = files[i];
            fileIndex++;
            
            filename = file.name;
            if (this.ignoreFiles.indexOf(filename) == -1) {
                if (file.size > MAX_UPLOAD_SIZE_FILE) {
                    files_too_big.push(filename)
                }
                
                // if more then one file is too big there is no need to check any further
                if (files_too_big.length > 1)
                    return this.showFilesTooBigDialog(files_too_big);
                
                this.totalNumUploads++;
                if (filesInQueue < MAX_VISIBLE_UPLOADS) {
                    this.uploadIndex = fileIndex;
                    filesInQueue++;
                    filesToAddToQueue.push(file);
                } 
            }
        }
        
        if (files_too_big.length)
            return this.showFilesTooBigDialog(files_too_big);

        this.uploadFiles.push({
            targetFolder: node,
            queue: files
        });
        if ((l=filesToAddToQueue.length)) {
            for (i = 0; i < l; i++) {
                this.addToQueue(filesToAddToQueue[i], node);
            }
        }
        if (!this.uploadInProgress) {
            // show upload activity list
            boxUploadActivity.show();
            btnCancelUploads.show();
            
            this.uploadNextFile();
        }
    },
    
    showFilesTooBigDialog: function(files) {
        if (files.length == 1) {
            util.alert(
                "Maximum file-size exceeded", "A file exceeds our upload limit of 50MB per file.",
                "Please remove the file '" + files[0] + "' from the list to continue."
            );
        }
        else {
            util.alert(
                "Maximum file-size exceeded", "Some files exceed our upload limit of 50MB per file.",
                "Please remove all files larger than 50MB from the list to continue."
            );
        }
    },
    
    checkSelectableFile: function(event) {
        if (event.selected && event.selected.getAttribute("type") == "fileupload") 
            return false;
    },
    
    addToQueue: function(file, targetFolder, callback) {
        // add files in dirty state
        file.targetFolder = targetFolder;
        var path = file.targetFolder.getAttribute("path");
        
        var filepath;
        if (file.webkitRelativePath) {
            filepath = file.webkitRelativePath && file.webkitRelativePath.split("/");
            filepath.pop(); // remove filename from path
            filepath = filepath.join("/");
        }
        
        if (!filepath)
            file.path = path;
        else
            file.path = path + "/" + filepath;
        
        // add file to upload activity list
        this.addToQueueList(file);
        this.uploadQueue.push(file);
        
        callback && callback();
    },
    
    // add file to file tree
    addToFileTree: function(file) {
        var filename = apf.escapeXML(file.name)
        var path = apf.escapeXML(file.path);
        
        var treeNode = trFiles.getModel().queryNode("//file[@path='" + path + "/" + filename + "']");
        if (treeNode)
            apf.xmldb.removeNode(treeNode);
            
        var xmlNode = "<file type='fileupload'" +
            " name='" + filename + "'" +
            " path='" + path + "/" + filename + "'" +
        "/>";
        
        apf.xmldb.appendChild(file.targetFolder, apf.getXml(xmlNode));
        //trFiles.add(xmlNode, file.targetFolder);
        file.treeNode = trFiles.queryNode("//file[@path='" + path + "/" + filename + "'][@name='" + filename + "']");	
    },
    
    //add file to upload activity list
    addToQueueList: function(file) {
        var queueNode = '<file name="' + apf.escapeXML(file.name) + '" />';
        file.queueNode = mdlUploadActivity.appendXml(queueNode);
    },
    
    removeFromQueue: function(name) {
        var file;
        for (var i = 0, l = this.uploadQueue.length; i < l; i++) {
            file = this.uploadQueue[i];
            if (file.name == name) {
                this.uploadQueue.splice(i, 1);
                apf.xmldb.removeNode(file.queueNode);
                uploadactivityNumFiles.$ext.innerHTML = "(" + --this.totalNumUploads + ")";
                break;
            }
        }
    },
    
    removeCurrentUploadFile: function() {
        var file = this.currentFile;
        apf.xmldb.removeNode(file.queueNode);
        //apf.xmldb.removeNode(file.treeNode);
        if (!mdlUploadActivity.data.childNodes.length) {
            boxUploadActivity.hide();
        }
    },
    
    // remove queued items from upload activity list that are completed uploading but were not removed yet
    // because user had his mousecursor on the list.
    clearCompletedUploads: function() {
        var _self = this;
        var completedUploads = mdlUploadActivity.queryNodes("file[@progress='100']");
        apf.asyncForEach(completedUploads, function(item, next) {
            if (_self.lockHideQueue)
                return;
            setTimeout(function() {    
                apf.xmldb.removeNode(item);
                next();
            }, 200);
        }, function() {
            
        });
    },
    
    uploadNextFile: function() {
        if (this.cancelAllUploads)
            return;
        
        var _self = this;
        uploadactivityNumFiles.$ext.innerHTML = "(" + this.totalNumUploads + ")";
        var file = this.currentFile = this.uploadQueue.shift();
        
        // check if there is a file to upload
        if (file) {
            var targetPath = file.targetFolder.getAttribute("path");
            var filepath;
            if (file.webkitRelativePath) {
                filepath = file.webkitRelativePath && file.webkitRelativePath.split("/");
                filepath.pop(); // remove filename from path
                filepath = filepath.join("/");
            }
            this.uploadInProgress = true;
            
            if (this.hideUploadActivityTimeout) {
                clearTimeout(this.hideUploadActivityTimeout);
                this.hideUploadActivityTimeout = null;
            }
        
            /** Chrome, Firefox */
            if (apf.hasFileApi) {
                function checkFileExists(exists) {
                    if (exists) {
                        if (_self.existingOverwriteAll) {
                            _self.overwrite();
                        }
                        else if (_self.existingSkipAll) {
                            _self.removeCurrentUploadFile();
                            _self.uploadNextFile();
                        }
                        else {
                            winUploadFileExists.show();
                            if (_self.uploadQueue.length) {
                                btnUploadOverwriteAll.show();
                                btnUploadSkipAll.show();
                            }
                            else {
                                btnUploadOverwriteAll.hide();
                                btnUploadSkipAll.hide();
                            }
                            uploadFileExistsMsg.$ext.innerHTML = "\"" + apf.escapeXML(file.name) + "\" already exists, do you want to replace it? Replacing it will overwrite its current contents.";
                        }
                    }
                    else {
                        upload();
                    }
                }
                
                function upload() {
                    var file = _self.currentFile;
                    
                    var targetFolder;
                    if (filepath) {
                        targetFolder = trFiles.getModel().data.selectSingleNode("//folder[@path='" + apf.escapeXML(targetPath + "/" + filepath) + "/" + apf.escapeXML(filepath) + "']");
                        
                        // folder not exist yet, create first
                        if (!targetFolder) {
                            var subfolder;
                            var currentPath = targetPath;
                            
                            var folders = filepath.split("/");
                            apf.asyncForEach(folders, function(folder, next) {
                                currentPath += "/" + folder;
                                subfolder = trFiles.getModel().data.selectSingleNode("//folder[@path='" + apf.escapeXML(currentPath) + "']");
                        
                                // subfolder is already created
                                if (subfolder) {
                                    trFiles.select(subfolder);
                                    next();
                                }
                                // subfolder does not exists, create first
                                else {
                                    fs.createFolder(folder, trFiles, true, function(folder) {
                                        if (!folder) {
                                            _self.removeFromQueue(file.name);
                                            _self.uploadNextFile();
                                        }
                                        // check if there are subfolders to be created
                                        subfolder = folder;
                                        next();
                                    });
                                }
                            }, function() {
                                if (_self.ignoreFiles.indexOf(file.name) > -1)
                                    return _self.uploadNextFile();
                                    
                                uploadNext(subfolder);
                            });
                        }
                        else {
                            uploadNext(targetFolder);
                        }
                    }
                    else {
                        uploadNext();
                    }
                    
                    function uploadNext(targetFolder) {
                        if (targetFolder)
                            file.targetFolder = targetFolder;
                        
                        _self.addToFileTree(file);
                        
                        var node = file.queueNode;
                        apf.xmldb.setAttribute(node, "progress", 0);
                        
                        if (!_self.worker)
                            _self.initWorker();
                            
                        _self.worker.postMessage({cmd: 'connect', id: file.name, file: file, path: file.targetFolder.getAttribute("path")});
                    }
                }
                _self.upload = upload;
                
                // check if file already exists in gotofile arraySearchResults
                /*
                var rootPath = trFiles.root.firstChild.getAttribute("path");
                var checkFilepath = (targetPath + (filepath ? "/" + filepath : "")).replace(rootPath, "") || "/";
                if (require("ext/gotofile/gotofile").arraySearchResults.indexOf(checkFilepath + file.name) > -1)
                    checkFileExists(true);
                else
                    upload();
                */
                
                fs.exists(apf.escapeXML(targetPath + (filepath ? "/" + filepath : "")) + "/" + file.name, checkFileExists);
            }
        }
        // no files in queue, upload completed
        else {
            uploadDone();
        }
        
        function uploadDone() {
            _self.uploadInProgress = false;
            _self.existingOverwriteAll = false;
            _self.existingSkipAll = false;
            btnCancelUploads.hide();
            (davProject.realWebdav || davProject).setAttribute("showhidden", settings.model.queryValue("auto/projecttree/@showhidden"));
            require("ext/tree/tree").refresh();
            _self.hideUploadActivityTimeout = setTimeout(function() {
                mdlUploadActivity.load("<data />");
                boxUploadActivity.hide();
            }, 5000);
        }
    },
    
    skip: function() {
        this.removeCurrentUploadFile();
        this.uploadNextFile();
    },
    
    skipAll: function() {
        this.existingSkipAll = true;
        this.skip();
    },
    
    overwrite: function() {
        var file = this.currentFile;
        var node = file.targetFolder;
        var path     = node.getAttribute("path");
        var filename = file.name;
        var treeNode = trFiles.getModel().queryNode("//file[@path='" + path + "/" + filename + "']");
        if (treeNode)
            apf.xmldb.removeNode(treeNode);

        fs.remove(path + "/" + filename, this.upload);
    },
    
    overwriteAll: function() {
        this.existingOverwriteAll = true;
        this.overwrite();
    },
    
    onProgress: function(perc) {
        if (this.cancelAllUploads)
            return;
            
        if(!this.currentFile) return;    
        var total = Math.floor(perc * 100);
        var node = this.currentFile.queueNode;
        var curPerc = node.getAttribute("progress")
        apf.xmldb.setAttribute(node, "progress", Math.max(total, curPerc));
    },
    
    onComplete: function() {
        if (this.cancelAllUploads) {
            this.cancelAllUploads = false;
            return;
        }
        
        var _self = this;
        var file = this.currentFile;
        //var path = file.targetFolder.getAttribute("path");
        this.totalNumUploads--;
        apf.xmldb.setAttribute(file.queueNode, "progress", "100");
        
        apf.xmldb.setAttribute(file.treeNode, "type", "file");
        
        if (_self.openOnUpload) {
            if (file.size < MAX_OPENFILE_SIZE)
                ide.dispatchEvent("openfile", {doc: ide.createDocument(file.treeNode)});
        }
        
        setTimeout(function() {
            if (!_self.lockHideQueue && file.queueNode)
                apf.xmldb.removeNode(file.queueNode);
            else
                _self.lockHideQueueItems.push(file);
        }, 1000);
        
        this.addNextFileToQueue();
        
        return _self.uploadNextFile();
    },
    
    addNextFileToQueue: function() {
        if (!this.uploadFiles[0] || !this.uploadFiles[0].queue[this.uploadIndex])
            this.uploadFiles.shift();
        
        if (!this.uploadFiles.length)
            return;
            
        // add next file to queue
        var nextfile;
        if (nextfile = this.uploadFiles[0].queue[++this.uploadIndex]) {
            if (this.ignoreFiles.indexOf(nextfile.name) > -1)
                return this.addNextFileToQueue();

            this.addToQueue(nextfile, this.uploadFiles[0].targetFolder);
        }
        else {
            this.uploadFiles.shift();
            this.uploadIndex = 0;
            return this.addNextFileToQueue();
        }
    },
    
    cancelAll: function() {
        this.cancelAllUploads = true;
        this.worker.postMessage({cmd: 'cancelall'});
        this.uploadFiles = [];
        this.uploadQueue = [];
        
        mdlUploadActivity.clear();
        boxUploadActivity.hide();
        
        (davProject.realWebdav || davProject).setAttribute("showhidden", settings.model.queryValue("auto/projecttree/@showhidden"));
        if (this.currentFile.treeNode)
            apf.xmldb.removeNode(this.currentFile.treeNode);
    },
    
    getFormData: function(file) {
        var form = new FormData();
        form.append("upload", file);
        
        return form;
    },
    
    
    enable : function(){
        this.nodes.each(function(item){
            item.enable();
        });
    },

    disable : function(){
        this.nodes.each(function(item){
            item.disable();
        });
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
    }
});

});
Example #13
0
module.exports = (function () {
    
    var activeTimeout = null;
    var windowHtml = null;
    var datagridHtml = null;
    var currentExpression = null;
    
    var hook = function () {
        var self = this;
        ext.initExtension(self);
        
        // listen to changes that affect the debugger, so we can toggle the visibility based on this
        stRunning.addEventListener("prop.active", checkDebuggerActive);
        stDebugProcessRunning.addEventListener("prop.active", checkDebuggerActive);
    };
    
    var init = function () {
        // get respective HTML elements
        windowHtml = winLiveInspect.$ext;
        datagridHtml = dgLiveInspect.$ext;
        winLiveInspect.addEventListener("prop.visible", function(e) {
            // don't track when hiding the window
            if (!e.value)
                return;
            ide.dispatchEvent("track_action", {
                type: "live inspect code",
                expression: currentExpression || "no expression available yet."
            });
        });
        
        // when hovering over the inspector window we should ignore all further listeners
        apf.addListener(datagridHtml, "mouseover", function() {
            if (activeTimeout) {
                clearTimeout(activeTimeout);
                activeTimeout = null;
            }
        });
        
        ide.addEventListener("language.worker", function(e){
            // listen to the worker's response
            e.worker.on("inspect", function(event) {
                if (!event || !event.data) {
                    winLiveInspect.hide();
                    return;
                }
                
                // create an expression that the debugger understands
                var expression = event.data;
                if (expression) {
                    liveWatch(expression);
                }
            });
        });
        
        // bind mouse events to all open editors
        ide.addEventListener("afteropenfile", function (e) {
            var editor = e.doc.$page && e.doc.$page.$editor;

            if (editor && editor.ceEditor) {
                var ceEditor = editor.ceEditor;
                
                ceEditor.$editor.addEventListener("mousemove", onEditorMouseMove);
                
                // when you click, or change the cursor position, then hide the window
                ceEditor.$editor.addEventListener("mousedown", onEditorClick);
                ceEditor.getSession().getSelection().addEventListener("changeCursor", onEditorClick);
            }
        });
        
        // we should track mouse movement over the whole window
        apf.addListener(document, "mousemove", onDocumentMouseMove);
        
        // yes, this is superhacky but the editor function in APF is crazy
        apf.addListener(datagridHtml, "dblclick", initializeEditor);
        
        // when collapsing or expanding the datagrid we want to resize
        dgLiveInspect.addEventListener("expand", resizeWindow);
        dgLiveInspect.addEventListener("collapse", resizeWindow);
    };
    
    var resizeWindow = function () {
        var gridRows = datagridHtml.querySelectorAll(".row");
        // iterate over all properties
        var rows = Object.keys(gridRows)
            .filter(function (r) { return !isNaN(r); }) // filter non numeric properties
            .map(function (r) { return gridRows[r]; }) // map them into real objects
            .filter(function (r) { return r.offsetHeight > 0; }); // check whether they are visible
        
        // if we have any rows
        if (rows && rows.length) {
            // determine height based on first one
            var height = rows[0].offsetHeight * rows.length;
            
            // add border of the container
            height += (windowHtml.offsetHeight - windowHtml.scrollHeight);
            
            // find header
            var header = datagridHtml.querySelector(".headings");
            if (header) {
                height += header.offsetHeight;
            }
            
            // we don't want this to fall of the screen
            var maxHeight = (window.innerHeight - winLiveInspect.$ext.offsetTop) - 30;
            if (height > maxHeight) {
                height = maxHeight;
            }
            
            // update height
            winLiveInspect.setAttribute("height", height);
        }
    };
        
    /**
     * WARNING this is a piece of junk
     * Initialize an editor in the place of the 'value' field when doubleclicking
     */
    var initializeEditor = function (ev) {
        // get the real clicked element
        var target = ev.target;
        
        // only interested in the node with index 1
        if (target.tagName === "U" /* its in an <u> */
          && target.parentNode.parentNode.childNodes[1] === target.parentNode /* [1] index */
          && !target.parentNode.hid /* and no header */) {
              
            // bug in APF? When having only 1 item the 'selected' property isnt set properly
            var selected = dgLiveInspect.selected;
            if (!selected && dgLiveInspect.getModel().data.childNodes.length === 1) {
                // because you just doubleclicked an item, well just grab the only one
                selected = dgLiveInspect.getModel().data.childNodes[0]; 
            }
              
            // check whether we are able to edit this item
            if (!inspector.isEditable(selected)) {
                return;
            }
            
            // V8 debugger cannot change variables that are locally scoped, so we need at least 
            // one parent property.
            if (inspector.calcName(selected, true).indexOf('.') === -1) {
                return;
            }
            
            // get current display property
            var originalDisplay = target.style.display;
            
            // create new simple input field
            var edit = document.createElement("input");
            edit.type = "text";
            edit.value = target.innerText;
            edit.style.width = "98%";
            edit.style.outline = "0";
            edit.style.border = "solid 1px gray";
            edit.style.height = "13px";
            edit.style["margin-top"] = "1px";
            
            // update variable
            var onBlur = function () {
                // remove to stop further prop
                edit.removeEventListener("blur", onBlur);
                
                // test for correct value
                if (!inspector.validateNewValue(selected, this.value)) {
                    alert("invalid value for type " + selected.getAttribute("type"));
                    return false;
                }
                
                // remove the texteditor
                this.parentNode.removeChild(this);
                
                // restore the label
                target.style.display = originalDisplay;
                target.innerText = this.value;
                
                inspector.setNewValue(selected, this.value, function (res) { });
            };
            
            // when blurring, update
            apf.addListener(edit, "blur", onBlur);
            
            // on keydown, same same
            apf.addListener(edit, "keydown", function(ev) {
                if (ev.keyCode === 27 || ev.keyCode === 13) { // tab or enter
                    return onBlur.call(this);
                }
                if (ev.keyCode === 32) {  // somewhere in APF the space is captured; no clue why
                    this.value += " "; // this is super lame, but better than nothing
                }
                return true;
            });
            
            // now hide the cur value
            target.style.display = "none";
            // and append textbox
            target.parentNode.appendChild(edit);
            
            // focus
            edit.focus();
        }
    };
    
    /**
     * Check whether the debugger is attached & on a breakpoint
     */
    var checkDebuggerActive = function () {
        if (!stRunning.active && stDebugProcessRunning.active) {
            // debugger running
        }
        else if (self.winLiveInspect) {
            winLiveInspect.hide();
        }
    };
    
    /**
     * Determine whether the current file is the current frame where the 
     * debugger is in.
     */
    var isCurrentFrame = function(){
        var frame, page = tabEditors.getPage();
        
        if (self.dgStack)
            frame = dgStack.selected;
        else
            frame = mdlDbgStack.queryNode("frame[@index=0]");
        
        if (!frame)
            return false;
        
        var scriptName = frame.getAttribute("script");
        
        // I have to do a fairly weak filename compare. 
        // An improvement is to store the full path in the stack model.
        if (page.getModel().queryValue("@path")
            .substr(ide.davPrefix.length + 1) != scriptName)
            return false;
        
        var line = frame.getAttribute("line");
        var column = frame.getAttribute("column");
        //@todo check if we are still in the current function
        
        return true;
    }
    
    /**
     * onMouseMove handler that is being used to show / hide the inline quick watch
     */
    var onEditorMouseMove = function (ev) {
        if (activeTimeout) {
            clearTimeout(activeTimeout);
            activeTimeout = null;
        }
        
        if (!stRunning.active && stDebugProcessRunning.active) {
            activeTimeout = setTimeout(function () {
                if (!isCurrentFrame())
                    return;
                
                var pos = ev.getDocumentPosition();
                ide.dispatchEvent("liveinspect", { row: pos.row, col: pos.column });
                
                // hide it, and set left / top so it gets positioned right when showing again
                winLiveInspect.hide();
                windowHtml.style.left = ev.clientX + "px";
                windowHtml.style.top = (ev.clientY + 8) + "px";
            }, 750);
        }
    };
    
    /**
     * onDocumentMove handler to clear the timeout
     */
    var onDocumentMouseMove = function (ev) {
        if (!activeTimeout) {
            return;   
        }
        
        // see whether we hover over the editor or the quickwatch window
        var mouseMoveAllowed = false;
        
        var eles = [ ceEditor, winLiveInspect ];
        // only the visible ones
        eles.filter(function (ele) { return ele.visible; })
            .map(function (ele) { return ele.$ext; }) // then get the HTML counterpart
            .forEach(function (ele) {
                // then detect real position
                var position = apf.getAbsolutePosition(ele, document.body);
                var left = position[0];
                var top = position[1];
                
                // x boundaries
                if (ev.pageX >= left && ev.pageX <= (left + ele.offsetWidth)) {
                    // y boundaries
                    if (ev.pageY >= top && ev.pageY <= (top + ele.offsetHeight)) {
                        // we are in the editor, so return; this will be handled
                        mouseMoveAllowed = true;
                    }
                }            
            });
        
        if (mouseMoveAllowed) return;
                
        // not in the editor?
        if (winLiveInspect.visible) {
            // if we are visible, then give the user 400 ms to get back into the window
            // otherwise hide it
            activeTimeout = setTimeout(function () {
                winLiveInspect.hide();
            }, 750);
        }
        else {
            // if not visible? then just clear the timeout
            clearTimeout(activeTimeout);
            activeTimeout = null;
        }
    };
    
    /**
     * When clicking in the editor window, hide live inspect
     */
    var onEditorClick = function (ev) {
        winLiveInspect.hide();
    };
    
    /**
     * Execute live watching
     */
    var liveWatch = function (expr) {
        // already visible, and same expression?
        if (winLiveInspect.visible && expr === currentExpression) {
            return;
        }
        
        // if there is any modal window open, then don't show
        var windows = getNumericProperties(document.querySelectorAll(".winadv") || {}).filter(function (w) {
            return w.style.display !== "none" && w.style.visibility !== "hidden";
        });
        if (windows.length) {
            return;
        }
        
        // if context menu open, then also disable
        if (mnuCtxEditor && mnuCtxEditor.visible) {
            return;
        }
        
        // evaluate the expression in the debugger, and receive model as callback
        inspector.evaluate(expr, function (model) {
            // bind it to the datagrid
            var root  = apf.getXml("<data />");
            apf.xmldb.appendChild(root, model);
            dgLiveInspect.getModel().load(root);
            
            // clean UI to remove selected elements
            dgLiveInspect.selected = null; 
            dgLiveInspect.selection = []; 
            dgLiveInspect.sellength = 0;
            
            // store it
            currentExpression = expr;        
                        
            // show window
            winLiveInspect.show();
            
            // resize the window
            resizeWindow();
        });
    };
    
    var getNumericProperties = function (obj) {
        return Object.keys(obj).filter(function (k) { return !isNaN(k); }).map(function (k) { return obj[k]; });
    };
    
    // public interfaces
    return ext.register("ext/language/liveinspect", {
        hook    : hook,
        init    : init,
        name    : "Live inspect",
        dev     : "Ajax.org",
        type    : ext.GENERAL,
        alone   : true,
        markup  : markup,
        deps    : [ debuggerPlugin ],
        skin    : {
            id   : "inlinedg",
            data : skin
        }
    });
} ());
Example #14
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var markup = require("text!ext/settings/settings.xml");
var template = require("text!ext/settings/template.xml");
var panels = require("ext/panels/panels");
var skin = require("text!ext/settings/skin.xml");

module.exports = ext.register("ext/settings/settings", {
    name    : "Preferences",
    dev     : "Ajax.org",
    alone   : true,
    type    : ext.GENERAL,
    markup  : markup,
    skin    : skin,
    commands : {
        "showsettings": {hint: "open the settings window"}
    },
    hotitems: {},

    nodes : [],

    save : function() {
        var _self = this;
        clearTimeout(this.$customSaveTimer);

        this.$customSaveTimer = setTimeout(function(){
            ide.dispatchEvent("savesettings", {model : _self.model});
            _self.saveToFile();
        }, 100);
    },

    saveToFile : function() {
        ide.send(JSON.stringify({
            command: "settings",
            action: "set",
            settings: this.model.data && apf.xmldb.cleanXml(this.model.data.xml) || ""
        }));
    },

    saveSettingsPanel: function() {
        var pages   = self.pgSettings ? pgSettings.getPages() : [],
            i       = 0,
            l       = pages.length,
            changed = false;
        for (; i < l; ++i) {
            if (!pages[i].$at) continue;
            if (pages[i].$at.undolength > 0) {
                pages[i].$commit(pages[i]);
                changed = true;
            }
        }
        if (ide.dispatchEvent("savesettings", {
            model : this.model
        }) !== false || changed)
            this.saveToFile();
    },

    addSection : function(tagName, name, xpath, cbCommit){
        var node = this.model.queryNode(xpath + "/" + tagName);
        if (!node)
            this.model.appendXml('<' + tagName + ' name="' + name +'" />', xpath);
    },

    load : function(){
        var _self = this;

        //@todo this should actually be an identifier to know that it was rights that prevented loading it
        ide.settings = ide.settings == "defaults" ? template : ide.settings;

        if (!ide.settings) {
            ide.addEventListener("socketMessage", function(e){
                if (e.message.type == "settings") {
                    var settings = e.message.settings;
                    if (!settings || settings == "defaults")
                        settings = template;
                    ide.settings =  settings;
                    _self.load();

                    ide.removeEventListener("socketMessage", arguments.callee);
                }
            });
            
            if (ide.onLine === true)
                ide.send(JSON.stringify({command: "settings", action: "get"}));
            return;
        }

        try {
            this.model.load(ide.settings);
        } catch(e) {
            this.model.load(template);
        }

        ide.dispatchEvent("loadsettings", {
            model : _self.model
        });

        var checkSave = function() {
            if (ide.dispatchEvent("savesettings", {
                model : _self.model
            }) === true)
                _self.saveToFile();
        };
        this.$timer = setInterval(checkSave, 60000);

        apf.addEventListener("exit", checkSave);

        ide.addEventListener("$event.loadsettings", function(callback) {
            callback({model: _self.model});
        });

        ide.removeEventListener("afteronline", this.$handleOnline);
    },

    hook : function(){
        panels.register(this);

        var btn = this.button = navbar.insertBefore(new apf.button({
            skin    : "mnubtn",
            state   : true,
            "class" : "preferences",
            caption : "Preferences"
        }), navbar.firstChild);

        var _self = this;

        btn.addEventListener("mousedown", function(e){
            var value = this.value;
            if (navbar.current && (navbar.current != _self || value)) {
                navbar.current.disable(navbar.current == _self);
                if (value)
                    return;
            }

            panels.initPanel(_self);
            _self.enable(true);
        });

        this.model = new apf.model();

        ide.addEventListener("afteronline", this.$handleOnline = function(){
            _self.load();
        });
    },

    init : function(amlNode){
        this.panel = winSettings;

        /*winSettings.addEventListener("hide", function(){
            colLeft.$ext.style.minWidth = "0px"; //hack
        });

        winSettings.addEventListener("show", function() {
            colLeft.$ext.style.minWidth = "215px"; //hack
        });*/

        colLeft.appendChild(winSettings);
    },

    showsettings: function(e){
        panels.initPanel(this);
        this.enable();
        return false;
    },

    saveSettings: function() {
        winSettings.hide();
        this.saveSettingsPanel();
    },

    applySettings: function() {
        this.saveSettingsPanel();
    },

    cancelSettings: function() {
        winSettings.hide();
        var pages = pgSettings.getPages(),
            i     = 0,
            l     = pages.length;
        for (; i < l; ++i) {
            if (!pages[i].$at) continue;
            pages[i].$at.undo(-1);
        }
    },

    enable : function(noButton){
        winSettings.show();
        colLeft.show();
        if (!noButton) {
            this.button.setValue(true);
            if(navbar.current && (navbar.current != this))
                navbar.current.disable(false);
        }
        splitterPanelLeft.show();
        navbar.current = this;
    },

    disable : function(noButton){
        if (self.winSettings)
            winSettings.hide();
        if (!noButton)
            this.button.setValue(false);

        splitterPanelLeft.hide();
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
    }
});

});
Example #15
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var panels = require("ext/panels/panels");
var editors = require("ext/editors/editors");
var Parser = require("ext/console/parser");
var Logger = require("ext/console/logger");
var Trie = require("ext/console/trie");
var Hints = require("ext/console/hints");
var css = require("text!ext/console/console.css");
var markup = require("text!ext/console/console.xml");
// Some hardcoded commands and their responses
var Output = require("ext/console/output");

var trieCommands;
var commands   = {};
var cmdTries   = {};
var cmdFetched = false;
var cmdBuffer  = "";
var lastSearch = null;
var parser = new Parser();

var KEY_TAB = 9;
var KEY_CR = 13;
var KEY_UP = 38;
var KEY_ESC = 27;
var KEY_DOWN = 40;
var actionCodes = [KEY_TAB, KEY_CR, KEY_UP, KEY_ESC, KEY_DOWN];

var cmdHistory = {
    _history: [],
    _index: 0,

    push: function(cmd) {
        this._history.push(cmd);
        this._index += 1;
    },
    length: function() {
        return this._history.length;
    },
    getNext: function() {
        this._index += 1;
        var cmd = this._history[this._index] || "";

        var maxLen = this._history.length - 1;
        if (this._index > maxLen)
            this._index = maxLen;

        return cmd;
    },
    getPrev: function() {
        this._index -= 1;
        if (this._index < 0)
            this._index = 0;

        return this._history[this._index];
    }
};

module.exports = ext.register("ext/console/console", {
    name   : "Console",
    dev    : "Ajax.org",
    type   : ext.GENERAL,
    alone  : true,
    markup : markup,
    css    : css,

    excludeParent : true,
    commands : {
        "help": {
            hint: "show general help information and a list of available commands"
        },
        "clear": {
            hint: "clear all the messages from the console"
        },
        "switchconsole": {
            hint: "toggle focus between the editor and the console"
        },
        "send": {
            hint: "send a message to the server"
        }
    },

    help : function() {
        var words = trieCommands.getWords();
        var tabs = "\t\t\t\t";

        Logger.logNodeStream(
            words
                .filter(function(w) { return commands[w]; })
                .map(function(w) { return w + tabs + commands[w].hint; })
                .join("\n")
        );
    },

    clear : function() {
        this.inited && txtOutput.clear();
    },

    switchconsole : function() {
        if (apf.activeElement == txtConsoleInput) {
            if (window.ceEditor) {
                ceEditor.focus();
                this.disable();
            }
        }
        else {
            txtConsoleInput.focus()
        }
    },

    send : function(data) {
        ide.send(data.line.replace(data.command,"").trim());
        return true;
    },

    showOutput : function(){
        tabConsole.set(1);
    },

    jump: function(path, row, column) {
        row = parseInt(row.slice(1), 10);
        column = column ? parseInt(column.slice(1), 10) : 0;
        editors.showFile(path, row, column);
    },

    getCwd: function() {
        return this.$cwd && this.$cwd.replace("/workspace", ide.workspaceDir);
    },

    write: function(aLines) {
        if (typeof aLines == "string")
            aLines = aLines.split("\n");

        aLines.forEach(function(line) { Logger.log(line, "log"); });

        Logger.log("", "divider");
    },

    keyupHandler: function(e) {
        if (actionCodes.indexOf(e.keyCode) === -1) {
            return this.commandTextHandler(e);
        }
    },

    keydownHandler: function(e) {
        if (actionCodes.indexOf(e.keyCode) !== -1) {
            return this.commandTextHandler(e);
        }
    },

    commandTextHandler: function(e) {
        var line = e.currentTarget.getValue();
        if (cmdBuffer === null || (cmdHistory._index === 0 && cmdBuffer !== line)) {
            cmdBuffer = line;
        }

        parser.parseLine(line);

        var code = e.keyCode;
        var hisLength = cmdHistory.length();
        var hintsVisible = Hints.visible();

        if (code === KEY_UP) { //UP
            if (hintsVisible) {
                Hints.selectUp();
            }
            else if (hisLength) {
                var newVal = cmdHistory.getPrev();

                if (newVal)
                    e.currentTarget.setValue(newVal);
            }
            return false;
        }
        else if (code === KEY_DOWN) { //DOWN
            if (hintsVisible) {
                Hints.selectDown();
            }
            else if (hisLength) {
                e.currentTarget.setValue(cmdHistory.getNext() || "");
            }
            return false;
        }
        else if (code === KEY_ESC && hintsVisible) {
            return Hints.hide();
        }
        else if (code != KEY_CR && code != KEY_TAB) {
            return this.autoComplete(e, parser, 2);
        }

        if (hintsVisible && Hints.selected())
            return Hints.click(Hints.selected());

        if (parser.argv.length === 0) {
            // no commmand line input
        }
        else if (parser.argQL[0]) {
            // first argument quoted -> error
            this.write("Syntax error: first argument quoted.");
        }
        else {
            if (code === KEY_TAB) {
                this.autoComplete(e, parser, 1);
                return false;
            }

            cmdHistory.push(line);
            cmdBuffer = null;
            e.currentTarget.setValue("");
            Hints.hide();

            Logger.log(this.getPrompt() + " " + parser.argv.join(" "), "prompt");
            this.enable();
            tabConsole.set("console");

            var cmd = parser.argv[0];

            // If there is a predefined (i.e. hardcoded) output for the current
            // command being executed in the CLI, show that.
            if (Output[cmd]) {
                var rest = parser.argv.join(" ").replace(new RegExp("^" + cmd), "").trim();
                var msg = Output[cmd][rest];

                if (Output[cmd][rest])
                    this.write(msg);
                else
                    this.write(Output[cmd].__default__.replace("%s", cmd));
            }
            else {
                if (cmd === "help") {
                    this.help();
                }
                else if (cmd === "clear") {
                    txtConsole.clear();
                }
                else {
                    var rest = parser.argv.join(" ").trim();
                    if (Output.general[rest]) {
                        this.write(Output.general[rest]);
                    }
                    else {
                        if (cmd.trim().charAt(0) == "!") {
                            var bandRE = /^\s*!/;
                            cmd = "bash";
                            parser.argv[0] = parser.argv[0].replace(bandRE, "");
                            line = line.replace(bandRE, "");
                        }

                        ide.dispatchEvent("track_action", {
                            type: "console",
                            cmd: cmd
                        });

                        var data = {
                            command: cmd,
                            argv: parser.argv,
                            line: line,
                            cwd: this.getCwd()
                        };

                        if (ext.execCommand(cmd, data) !== false) {
                            var evtName = "consolecommand." + cmd;
                            if (ide.dispatchEvent(evtName, { data: data }) !== false) {
                                if (!ide.onLine)
                                    this.write("Cannot execute command. You are currently offline.");
                                else
                                    ide.send(JSON.stringify(data));
                            }
                        }
                    }
                }
            }
        }
    },

    onMessage: function(e) {
        var res;
        var message = e.message;

        if (message.type == "node-data")
            return Logger.logNodeStream(message.data, message.stream, true);

        if (message.type != "result")
            return;

        switch (message.subtype) { 
            case "commandhints":
                var cmds = message.body;
                this.initCommands();
                for (var cmd in cmds) {
                    trieCommands.add(cmd);
                    commands[cmd] = cmds[cmd];
                    if (cmds[cmd].commands)
                        this.subCommands(cmds[cmd].commands, cmd);
                }
                cmdFetched = true;
                break;
            case "internal-autocomplete":
                res = message.body;
                var base = res.base || "";
                var blen = base.length;
                lastSearch = res;
                lastSearch.trie = new Trie();

                var m;
                for (var i = 0, l = res.matches.length; i < l; ++i) {
                    m = res.matches[i];
                    lastSearch.trie.add(m);
                    if (base && m.indexOf(base) === 0)
                        res.matches[i] = m.substr(blen - 1);
                }
                Hints.show(res.textbox, res.base || "", res.matches, null, res.cursor);
                break;
            case "cd":
                res = message.body;
                if (res.cwd) {
                    this.$cwd = res.cwd.replace(ide.workspaceDir, "/workspace");
                    this.write("Working directory changed.");
                }
                break;
            case "mkdir":
                res = message.body;
                ide.dispatchEvent("filecallback", {
                    type: "folder",
                    path: this.$cwd + "/" + res.argv[res.argv.length - 1]
                });
                break;
            case "rm":
                res = message.body;
                ide.dispatchEvent("filecallback");
                break;
            case "error":
                Logger.log(message.body);
                Logger.log("", "divider");
                break;
            default:
                res = message.body;
                if (res) {
                    if (res.out)
                        Logger.logNodeStream(res.out);
                    if (res.err)
                        Logger.logNodeStream(res.err);
                    if (res.code) // End of command
                        Logger.log("", "divider");
                }
                break;
        }

        ide.dispatchEvent("consoleresult." + message.subtype, {data: message.body});
    },

    setPrompt: function(cwd) {
        if (cwd)
            this.$cwd = cwd.replace(ide.workspaceDir, ide.davPrefix);

        return this.getPrompt();
    },

    getPrompt: function() {
        if (!this.username)
            this.username = (ide.workspaceId.match(/user\/(\w+)\//) || [,"guest"])[1];

        return "[" + this.username + "@cloud9]:" + this.$cwd + "$";
    },

    subCommands: function(cmds, prefix) {
        if (!cmdTries[prefix]) {
            cmdTries[prefix] = {
                trie: new Trie(),
                commands: cmds
            };
        }

        for (var cmd in cmds) {
            cmdTries[prefix].trie.add(cmd);
            if (cmds[cmd].commands)
                this.subCommands(cmds[cmd].commands, prefix + "-" + cmd);
        }
    },

    initCommands: function() {
        if (trieCommands)
            return;
        trieCommands = new Trie();
        apf.extend(commands, ext.commandsLut);
        for (var name in ext.commandsLut) {
            trieCommands.add(name);
            if (ext.commandsLut[name].commands)
                this.subCommands(ext.commandsLut[name].commands, name);
        }
    },

    autoComplete: function(e, parser, mode) {
        mode = mode || 2;
        if (mode === 1) {
            if (this.$busy) return;
            var _self = this;
            this.$busy = setTimeout(function(){clearTimeout(_self.$busy);_self.$busy = null;}, 100);
        }

        this.initCommands();

        // keycodes that invalidate the previous autocomplete:
        if (e.keyCode == 8 || e.keyCode == 46)
            lastSearch = null;

        var cmds = commands;
        var textbox = e.currentTarget;
        var val = textbox.getValue();
        var cursorPos = 0;

        if (!apf.hasMsRangeObject) {
            var input = textbox.$ext.getElementsByTagName("input")[0];
            cursorPos = input.selectionStart;
        }
        else {
            var range = document.selection.createRange();
            var r2    = range.duplicate();

            r2.expand("textedit");
            r2.setEndPoint("EndToStart", range);
            cursorPos = r2.text.length;
        }
        cursorPos -= 1;

        if (!cmdFetched) {
            // the 'commandhints' command retreives a list of available commands
            // from all the server plugins, to support git auto-completion, for
            // example.
            ide.send(JSON.stringify({
                command: "commandhints",
                argv: parser.argv,
                cwd: this.getCwd()
            }));
        }

        var base = parser.argv[0];
        if (typeof base != "string")
            return Hints.hide();

        // check for commands in first argument when there is only one argument
        // provided, or when the cursor on the first argument
        var root;
        var list = [];
        var len = parser.argv.length;
        if (len === 1 && cursorPos < base.length) {
            root = trieCommands.find(base);
            if (root)
                list = root.getWords();
        }
        else {
            var idx, needle, cmdTrie;
            var i = len - 1;

            for (; i >= 0; --i) {
                idx = val.indexOf(parser.argv[i]);
                if (idx === -1) //shouldn't happen, but yeah...
                    continue;

                if (cursorPos >= idx || cursorPos <= idx + parser.argv[i].length) {
                    needle = i;
                    break;
                }
            }
            if (typeof needle != "number")
                needle = 0;

            ++needle;
            while (needle >= 0 && !(cmdTrie = cmdTries[parser.argv.slice(0, needle).join("-")]))
                --needle

            if (cmdTrie) {
                base = parser.argv[needle];
                root = cmdTrie.trie.find(base);
                if (root) {
                    list = root.getWords();
                }
                else {
                    list = cmdTrie.trie.getWords();
                    // check for file/folder autocompletion:
                    if (list.length == 1 && list[0] == "[PATH]") {
                        //console.log("we have PATH, ", base, lastSearch);
                        if (base && lastSearch) {
                            list.splice(0, 1);
                            var newbase = base.split("/").pop();
                            if (!newbase) {
                                base = "";
                                list = lastSearch.trie.getWords();
                            }
                            else if (newbase.indexOf(lastSearch.base) > -1) {
                                // console.log("searching for ", newbase, base, "mode:", mode);
                                root = lastSearch.trie.find(newbase);
                                if (root) {
                                    // console.log("setting base ", base, "to", base, newbase);
                                    base = newbase;
                                    list = root.getWords();
                                }
                            }
                            if (!list.length) {
                                // we COULD do something special here...
                            }
                        }
                        else {
                            if (mode == 2)
                                list.splice(0, 1);
                            //base = "";
                        }
                        // adjust the argv array to match the current cursor position:
                        parser.argv = parser.argv.slice(0, needle);
                        parser.argv.push(base);
                        // else: autocompletion is sent to the backend
                        //console.log("directory found: ", base, list, "mode:", mode);
                    }
                    else {
                        base = "";
                    }
                }
                cmds = cmdTrie.commands;
                //console.log("list: ", list, base, parser.argv);
            }
        }

        if (list.length === 0)
            return Hints.hide();

        if (mode === 2) { // hints box
            Hints.show(textbox, base || "", list, cmds, cursorPos);
        }
        else if (mode === 1) { // TAB autocompletion
            var ins = base ? list[0].substr(1) : list[0];
            if (ins.indexOf("PATH]") != -1 && lastSearch && lastSearch.line == val && lastSearch.matches.length == 1)
                ins = lastSearch.matches[0].replace(lastSearch.base, "");
            if (ins.indexOf("PATH]") != -1) {
                ide.send(JSON.stringify({
                    command: "internal-autocomplete",
                    line   : val,
                    textbox: textbox.id,
                    cursor : cursorPos,
                    argv   : parser.argv,
                    cwd    : this.getCwd()
                }));
            }
            else {
                if (!!(cmds || commands)[base + ins])
                    ins += " "; // for commands we suffix with whitespace
                var newval = val.substr(0, cursorPos + 1) + ins + val.substr(cursorPos + 1);
                if (val != newval)
                    textbox.setValue(newval);
                lastSearch = null;
            }
            Hints.hide();
        }
    },

    /**** Init ****/

    hook : function(){
        panels.register(this);
        panels.initPanel(this);
    },

    init : function(amlNode){
        var _self = this
        this.panel = tabConsole;
        this.$cwd  = "/workspace";

        //Append the console window at the bottom below the tab
        mainRow.appendChild(winDbgConsole); //selectSingleNode("a:hbox[1]/a:vbox[2]").

        apf.importCssString((this.css || "") + " .console_date{display:inline}");

        stProcessRunning.addEventListener("activate", function() {
            _self.clear();
            _self.showOutput();
            _self.enable();
        });

        ide.addEventListener("socketMessage", this.onMessage.bind(this));
        ide.addEventListener("consoleresult.internal-isfile", function(e) {
            var data = e.data;
            var path = data.cwd.replace(ide.workspaceDir, ide.davPrefix);
            if (data.isfile)
                editors.showFile(path);
            else
                Logger.log("'" + path + "' is not a file.");
        });

        winDbgConsole.previousSibling.hide(); //que?

        function kdHandler(e){
            if (!e.ctrlKey && !e.metaKey && !e.altKey
              && !e.shiftKey && apf.isCharacter(e.keyCode))
                txtConsoleInput.focus()
        }

        txtOutput.addEventListener("keydown", kdHandler);
        txtConsole.addEventListener("keydown", kdHandler);
    },

    enable : function(fromParent){
        /*if (!this.panel)
            panels.initPanel(this);

        if (this.manual && fromParent)
            return;

        if (!fromParent)
            this.manual = true;*/

        this.mnuItem.check();
        tabConsole.show();

        if (winDbgConsole.height == 41)
            winDbgConsole.setAttribute("height", this.height || 200);
        winDbgConsole.previousSibling.show();

        apf.layout.forceResize();
        apf.setStyleClass(btnCollapseConsole.$ext, "btn_console_openOpen");

    },

    disable : function(fromParent){
        /*if (this.manual && fromParent || !this.inited)
            return;

        if (!fromParent)
            this.manual = true;*/

        this.mnuItem.uncheck();
        tabConsole.hide();

        if (winDbgConsole.height != 41)
            this.height = winDbgConsole.height;
        winDbgConsole.setAttribute("height", 41);
        winDbgConsole.previousSibling.hide();

        apf.layout.forceResize();
        apf.setStyleClass(btnCollapseConsole.$ext, '' , ['btn_console_openOpen']);
    },

    destroy : function(){
        winDbgConsole.destroy(true, true);
        panels.unregister(this);
    }
});

});
Example #16
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var noderunner = require("ext/noderunner/noderunner");
var panels = require("ext/panels/panels");
var settings = require("ext/settings/settings");
var menus = require("ext/menus/menus");
var tooltip = require("ext/tooltip/tooltip");
var dock = require("ext/dockpanel/dockpanel");
var save = require("ext/save/save");
var markup = require("text!ext/runpanel/runpanel.xml");
var markupSettings = require("text!ext/runpanel/settings.xml");
var cssString = require("text!ext/runpanel/style.css");
var commands = require("ext/commands/commands");

/*global stProcessRunning*/

module.exports = ext.register("ext/runpanel/runpanel", {
    name    : "Run Panel",
    dev     : "Ajax.org",
    type    : ext.GENERAL,
    alone   : true,
    offline : false,
    autodisable : ext.ONLINE | ext.LOCAL,
    markup  : markup,
    deps    : [noderunner],

    defaultWidth : 270,

    excludedTypes : {"xml":1, "html":1, "css":1, "txt":1, "png": 1, "jpg": 1, "gif": 1},

    nodes : [],
    model : new apf.model(),
    
    disableLut: {
        "terminal": true
    },

    hook : function(){
        if (ide.readonly)
            return;
        var _self = this;

        this.markupInsertionPoint = colLeft;

        panels.register(this, {
            position : 3000,
            caption: "Run & Debug",
            "class": "rundebug"
        });

        commands.addCommand({
            name: "run",
            "hint": "run or debug an application (stops the app if running)",
            "commands": {
                "[PATH]": {"hint": "path pointing to an executable. Autocomplete with [TAB]"}
            },
            bindKey: {mac: "F5", win: "F5"},
            exec: function () {
                if (stProcessRunning.active)
                    _self.stop();
                else
                    _self.run();
            }
        });

        commands.addCommand({
            name: "stop",
            "hint": "stop a running node program on the server",
            "commands": {
                "[PATH]": {"hint": "path pointing to an executable. Autocomplete with [TAB]"}
            },
            bindKey: {mac: "Shift-F5", win: "Shift-F5"},
            exec: function () {
                _self.stop();
            }
        });

        this.nodes.push(
            this.mnuRunCfg = new apf.menu({
                "id" : "mnuRunCfg",
                "onprop.visible" : function(e){
                    if (e.value) {
                        if (!this.populated) {
                            _self.$populateMenu();
                            this.populated = true;
                        }

                        if (!self.tabEditors
                          || tabEditors.length == 0
                          || _self.excludedTypes[tabEditors.getPage().id.split(".").pop()])
                            _self.mnuRunCfg.firstChild.disable();
                        else
                            _self.mnuRunCfg.firstChild.enable();
                    }
                }
            }),

            menus.$insertByIndex(barTools, new apf.splitbutton({
                id       : "btnRun",
                checked  : "[{require('ext/settings/settings').model}::auto/configurations/@debug]",
                icon     : "{this.checked ? 'run.png' : 'run.png'}",
                caption  : "{apf.isTrue(this.checked) ? 'Debug' : 'Run'}",
                command  : "run",
                visible  : "{!stProcessRunning.active and 1}",
                disabled : "{!!!ide.onLine}",
                submenu  : "mnuRunCfg"
            }), 100),

            menus.$insertByIndex(barTools, new apf.button({
                id       : "btnStop",
                icon     : "stop.png",
                caption  : "stop",
                width    : "52",
                tooltip  : "Stop",
                skin     : "c9-toolbarbutton-glossy",
                command  : "stop",
                visible  : "{stProcessRunning.active and 1}" ,
                disabled : "{!!!ide.onLine}"
            }), 200),

//            menus.$insertByIndex(barTools, new apf.divider({
//                skin : "c9-divider"
//            }), 300),

            this.model = new apf.model().load("<configurations />")
        );

        apf.setStyleClass(btnRun.$ext, "btnRun");
        apf.setStyleClass(btnStop.$ext, "btnStop");

        tooltip.add( btnRun.$button1, {
            message : "Run &amp; Debug your <span>Node.js</span> applications, or run your <span>PHP</span>, <span>Python</span>, or <span>Ruby</span> code.\
            For more help, check out our guided tour in the Help menu.\
            Want your language supported? Tweet us \
            <a href='http://twitter.com/Cloud9IDE' target='_blank'>@Cloud9IDE</a>!",
            width : "203px",
            timeout : 1000,
            hideonclick : true
        });

        var c = 0;
        menus.addItemToMenu(this.mnuRunCfg, new apf.item({
            caption  : "no run history",
            disabled : true
        }), c += 100);
        menus.addItemToMenu(this.mnuRunCfg, new apf.divider(), c += 100);
        menus.addItemToMenu(this.mnuRunCfg, new apf.item({
            caption : "Configure....",
            onclick : function(){
                _self.showRunConfigs(false);
            }
        }), c += 100);
        menus.addItemToMenu(this.mnuRunCfg, new apf.divider(), c += 100);
        menus.addItemToMenu(this.mnuRunCfg, new apf.item({
            caption : "Run in debug mode",
            type    : "check",
            checked : "[{require('ext/settings/settings').model}::auto/configurations/@debug]"
        }), c += 100);
        menus.addItemToMenu(this.mnuRunCfg, new apf.item({
            caption : "Auto show & hide debug tools",
            type    : "check",
            onclick : function(){
                _self.checkAutoHide();
            },
            checked : "[{require('ext/settings/settings').model}::auto/configurations/@autohide]"
        }), c += 100);

        settings.addSettings("General", markupSettings);

        ide.addEventListener("settings.load", function(e){
            settings.setDefaults("auto/node-version", [
                ["version", "auto"]
            ]);

            settings.setDefaults("general", [
                ["saveallbeforerun", "false"]
            ]);

            settings.setDefaults("auto/configurations", [
                ["debug", "false"],
                ["autohide", "true"]
            ]);

            var runConfigs = e.model.queryNode("auto/configurations");
            if (!runConfigs.selectSingleNode("config[@curfile]")) {
                var setLast = false;
                if (!e.model.queryNode("auto/configurations/config[@last='true']")) {
                    var config = e.model.queryNode("auto/configurations/config")
                    if (config)
                        apf.xmldb.setAttribute(config, "last", "true");
                    else
                        setLast = true;
                }

                var cfg = apf.n("<config />")
                    .attr("name", " (active file)")
                    .attr("curfile", "1");
                if (setLast)
                    cfg.attr("last", "true");
                runConfigs.insertBefore(cfg.node(), runConfigs.firstChild);
            }

            _self.model.load(runConfigs);
        });

        function setActiveFile(page){
            if (page && page.$model && page.$doc.getNode().getAttribute("ignore") !== "1") {
                var prefixRegex = new RegExp("^" + ide.davPrefix);
                var path = page.$model.data.getAttribute("path").replace(prefixRegex, "");
                _self.model.setQueryValue("config[@curfile]/@path", path);
                _self.model.setQueryValue("config[@curfile]/@name",
                    path.split("/").pop() + " (active file)");
            }
        }

        ide.addEventListener("init.ext/editors/editors", function(e) {
            setActiveFile(tabEditors.getPage());

            ide.addEventListener("tab.afterswitch", function(e){
                setActiveFile(e.nextPage);
            });

            ide.addEventListener("updatefile", function(e){
                setActiveFile(tabEditors.getPage());
            });
            
            ide.addEventListener("tab.afterswitch", function(e){
                _self.enable();
            });
            ide.addEventListener("closefile", function(e){
                if (tabEditors.getPages().length == 1)
                    _self.disable();
            });
        });

        var hasBreaked = false;
        stProcessRunning.addEventListener("deactivate", function(){
            if (!_self.autoHidePanel())
                return;

            var name = "ext/debugger/debugger";
            dock.hideSection(name, false);
            hasBreaked = false;

            /*var bar = dock.getBars("ext/debugger/debugger", "pgDebugNav")[0];
            if (!bar.extended)
                dock.hideBar(bar);*/
        });
        /*stProcessRunning.addEventListener("activate", function(){
            if (!_self.shouldRunInDebugMode() || !_self.autoHidePanel())
                return;

            var bar = dock.getBars("ext/debugger/debugger", "pgDebugNav")[0];
            if (!bar.extended)
                dock.showBar(bar);
        });*/
        ide.addEventListener("dbg.break", function(){
            if (!_self.shouldRunInDebugMode() || !_self.autoHidePanel() || hasBreaked)
                return;

            hasBreaked = true;

            var name = "ext/debugger/debugger";
            dock.showSection(name, false);

            var uId = dock.getButtons(name, "pgDebugNav")[0].uniqueId;
            if (dock.layout.isExpanded(uId) < 0)
                dock.layout.showMenu(uId);

            //var bar = dock.getBars("ext/debugger/debugger", "pgDebugNav")[0];
            //dock.expandBar(bar);
        });

        // When we are not in debug mode and we close a page it goes back to be
        // automatically opened when the debug process starts
        ide.addEventListener("init.ext/debugger/debugger", function(){
            tabDebug.getPages().concat(tabDebugButtons.getPages()).each(function(page){
                page.addEventListener("afterclose", function(e){
                    if (_self.autoHidePanel() && !stProcessRunning.active) {
                        this.$dockbutton.$dockData.hidden = 1;
                    }
                });
            });
        });
    },

    saveSelection : function() {
        var node = this.model.data.selectSingleNode('config[@last="true"]');
        if (node)
            node.removeAttribute("last");
        if (lstRunCfg.selected) {
            lstRunCfg.selected.setAttribute("last", "true");
            settings.save();
        }
        else
            lstRunCfg.select(lstRunCfg.$model.queryNode("//config"));
    },

    checkAutoHide : function(){
        /*var value = settings.model.queryValue("auto/configurations/@autohide");
        var bar = dock.getBars("ext/debugger/debugger", "pgDebugNav")[0];

        if (value && bar.cache && bar.cache.visible)
            dock.hideSection("ext/debugger/debugger");
        else if (!value && bar.cache && !bar.cache.visible)
            dock.showSection("ext/debugger/debugger");*/
    },

    init : function(amlNode){
        if (ide.readonly)
            return;
        var _self = this;

        apf.importCssString(cssString);

        this.panel = winRunPanel;
        this.nodes.push(winRunPanel);

        lstRunCfg.addEventListener("click", function(e){
            if (e.htmlEvent.target.tagName == "SPAN") {
                var xmlNode = apf.xmldb.findXmlNode(e.htmlEvent.target.parentNode.parentNode);
                this.remove(xmlNode);
            }
        });

        lstRunCfg.addEventListener("afterremove", function(e){
            _self.mnuRunCfg.childNodes.each(function(item){
                if (_self.mnuRunCfg.populated && item.node == e.args[0].xmlNode)
                    item.destroy(true, true);
            });
        });

        setTimeout(function() {
            if (lstRunCfg.$model)
                lstRunCfg.select(lstRunCfg.$model.queryNode("config[@last='true']"));
        });
    },

    duplicate : function() {
        var config = lstRunCfg.selected;
        if (!config)
            return;

        var duplicate = config.cloneNode(true);
        apf.b(config).after(duplicate);
        lstRunCfg.select(duplicate);
        winRunPanel.show();
    },

    addConfig : function() {
        var path, name, file = ide.getActivePageModel();
        var extension = "";
        
        if (file) {
            path  = file.getAttribute("path").slice(ide.davPrefix.length + 1); //@todo inconsistent
            name  = file.getAttribute("name").replace(/\.(js|py)$/,
                function(full, ext){ extension = ext; return ""; });
        }
        else {
            extension = name = path = "";
        }

        var cfg = apf.n("<config />")
            .attr("path", path)
            .attr("name", name)
            .attr("extension", extension)
            .attr("args", "").node();

        var node = this.model.appendXml(cfg);
        this.$addMenuItem(node);

        lstRunCfg.select(node);
    },

    showRunConfigs : function() {
        panels.activate(this);
    },

    autoHidePanel : function(){
        return apf.isTrue(settings.model.queryValue("auto/configurations/@autohide"));
    },

    shouldRunInDebugMode : function(){
        return apf.isTrue(settings.model.queryValue('auto/configurations/@debug'));
    },

    run : function(debug) {
        var node;

        if (window.winRunPanel && winRunPanel.visible)
            node = lstRunCfg.selected;
        else {
            node = this.model.queryNode("node()[@last='true']")
                || this.model.queryNode("config[@curfile]");
        }

        if (node.getAttribute("curfile")
          && this.excludedTypes[node.getAttribute("path").split(".").pop()]) {
            this.showRunConfigs(false);
            return;
        }

        this.runConfig(node, this.shouldRunInDebugMode());

        ide.dispatchEvent("track_action", {type: debug ? "debug" : "run"});
    },

    $populateMenu : function() {
        var menu = this.mnuRunCfg;

        var item = menu.firstChild;
        while (item && item.localName !== "divider") {
            menu.removeChild(item);
            item = menu.firstChild;
        }
        var divider = item;

        var configs = this.model.queryNodes("config");
        if (!configs.length)
            menu.insertBefore(new apf.item({disabled:true, caption: "No run history"}), divider);
        else {
            for (var i =  0, l = configs.length; i < l; i++) {
                this.$addMenuItem(configs[i], divider);
            }
        }
    },

    $addMenuItem : function(cfg, divider){
        var _self = this;

        if (this.mnuRunCfg.populated)
            return;

        if (!divider)
            divider = this.mnuRunCfg.getElementsByTagNameNS("", "divider")[0];

        this.mnuRunCfg.insertBefore(new apf.item({
            caption  : "[{this.node}::@name]",
            node     : cfg,
            type     : "radio",
            selected : "[{this.node}::@last]",
            onclick  : function() {
                _self.runConfig(this.node, _self.shouldRunInDebugMode());
                if (self.lstRunCfg)
                    lstRunCfg.select(this.node);
            }
        }), divider);
    },

    runConfig : function(config, debug) {
        //ext.initExtension(this);
        var model = settings.model;
        var saveallbeforerun = apf.isTrue(model.queryValue("general/@saveallbeforerun"));
        if (saveallbeforerun)
            save.saveall();

        if (debug === undefined)
            debug = config.parentNode.getAttribute("debug") == "1";

        var lastNode = apf.queryNode(config, "../node()[@last]");
        if (lastNode)
            apf.xmldb.removeAttribute(lastNode, "last");
        apf.xmldb.setAttribute(config, "last", "true");

        self["txtCmdArgs"] && txtCmdArgs.blur(); // fix the args cache issue #2763
        // dispatch here instead of in the implementation because the implementations
        // will vary over time
        ide.dispatchEvent("beforeRunning");
        var args = config.getAttribute("args");
        noderunner.run(
            config.getAttribute("path"),
            args ? args.split(" ") : [],
            debug,
            config.getAttribute("value")
        );
    },

    stop : function() {
        noderunner.stop();

        //dock.hideSection(["ext/run/run", "ext/debugger/debugger"]);
    },

    enable : function(){
        var page = tabEditors.getPage();
        var contentType = (page && page.getModel().data.getAttribute("contenttype")) || "";
        if(this.disableLut[contentType])
            return this.disable();
        this.$enable();
    },

    destroy : function(){
        commands.removeCommandsByName(["run", "stop"]);
        panels.unregister(this);
        this.$destroy();
    }
});

});
Example #17
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var panels = require("ext/panels/panels");
var markup = require("text!ext/testpanel/testpanel.xml");
var fs = require("ext/filesystem/filesystem");
var settings = require("core/settings");
var editors = require("ext/editors/editors");

module.exports = ext.register("ext/testpanel/testpanel", {
    name            : "Test Panel",
    dev             : "Ajax.org",
    alone           : true,
    type            : ext.GENERAL,
    markup          : markup,
    appliedFilter   : "all",
    nodes           : [],
    offline         : false,

    defaultWidth    : 290,

    hook : function(){
        var _self = this;

        this.markupInsertionPoint = colLeft;

        panels.register(this, {
            position : 4000,
            caption: "Test",
            "class": "testing"
        });

        ide.addEventListener("settings.load", function(e){
            settings.setDefaults("auto/testpanel", [
                ["autorun", "none"],
                ["type", "all"],
                ["showlibraries", "true"],
                ["autoexpand", "true"]
            ]);
        });

        ide.addEventListener("afterfilesave", function(e) {
            var autoRun = settings.model.queryValue("auto/testpanel/@autorun");

            if (dbg.state)
                return;

            if (autoRun == "none")
                return;
            if (autoRun == "selection" && _self.dgTestProject) {
                var sel = dgTestProject.getSelection();
                if (sel.length)
                    _self.run(sel);
            }
            else if (autoRun == "pattern") {
                var func = new Function('path', _self.getPattern());
                var list = func(e.node.getAttribute("path"));

                if (!list || list.dataType != apf.ARRAY) {
                    util.alert("Wrong output from pattern",
                        "Wrong output from pattern",
                        "Pattern did not generate list of strings");
                    return;
                }

                var nodes = [], node;
                list.forEach(function(path){
                    node = mdlTests.queryNode("//node()[@path="
                        + util.escapeXpathString(path) + "]");
                    if (node)
                        nodes.push(node);
                });

                if (nodes.length)
                    _self.run(nodes);
            }
        });
    },

    init : function() {

        btnTestRun.$ext.setAttribute("class", "light-dropdown");
        btnTestStop.$ext.setAttribute("class", btnTestStop.$ext.getAttribute("class") + " btnTestStop");
        winTestPanel.$ext.setAttribute("class", winTestPanel.$ext.getAttribute("class") + " testpanel");

        var _self  = this;

        this.panel = winTestPanel;
        this.nodes.push(winTestPanel, mnuRunSettings, stTestRun);

        ide.dispatchEvent("init.testrunner");

        mnuFilter.onitemclick = function(e){
            if (e.value && e.relatedNode.type == "radio")
                _self.filter(e.value);
        }

        var altKey;
        apf.addListener(document, "keydown", function(e){
            if ((e || event).keyCode == 18)
                altKey = true;
        });

        apf.addListener(document, "keyup", function(e){
            if ((e || event).keyCode == 18)
                altKey = false;
        });

        dgTestProject.addEventListener("afterchoose", function(e){
            var node = this.selected;
            if (!node || this.selection.length > 1)
                return;

            //Open
            if (altKey) {
                if (node.tagName != "file"
                  || !ide.onLine && !ide.offlineFileSystemSupport)
                    return;

                editors.gotoDocument({doc: ide.createDocument(node), origin: "testpanel"});

                //@todo choose a test or an assert should select that code
                //      inside ace.
            }
            //Run
            else {
                if ("file|test|repo".indexOf(node.tagName) == -1 || !ide.onLine)
                    return;

                _self.run([node]);
            }
        });

        ide.addEventListener("afteroffline", function(){
            btnTestRun.disable();
            _self.stop(true);
        });

        ide.addEventListener("afteronline", function(){
            btnTestRun.enable();
        });

        this.submodules = [];
        fs.readFile("/workspace/.git/config", function(data){
            data.replace(/\[submodule "([^"]*)"\]/g, function(s, m){
                _self.addRepo(m);
            });
        });
    },

    addRepo : function(name){
        var doc = mdlTests.data.ownerDocument;
        var node = doc.createElement("repo");
        node.setAttribute("name", name);
        mdlTests.appendXml(node);
        this.submodules.push(name);

        return node;
    },

    getPattern : function(){
        return settings.model.queryValue("auto/testpanel/pattern/text()") ||
            "// Enter any code below that returns the paths of the tests in an array of strings.\n"
            + "// You have access to the 'path' variable.\n"
            + "// Save this file to store the pattern.\n"
            + "var tests = [];\n"
            + "return tests.pushUnique(\n"
            + "    path.replace(/(?:_test)?\.js$/, \"_test.js\"),\n"
            + "    path.replace(/(?:_Test)?\.js$/, \"Test.js\")\n"
            + ");";
    },

    editAutoRunPattern : function(){
        var node = apf.n("<file />")
            .attr("name", "Pattern.js")
            .attr("path", "/workspace/.c9.test.pattern")
            .attr("changed", "1")
            .attr("newfile", "1")
            .node();

        var pattern = this.getPattern();

        var doc = ide.createDocument(node);
        doc.cachedValue = pattern;

        editors.gotoDocument({doc: doc, node: node, origin: "testpanel"});

        ide.addEventListener("beforefilesave", function(e){
            if (e.node == node) {

                var value = doc.getValue();
                settings.model.setQueryValue("auto/testpanel/pattern/text()", value);
                node.removeAttribute("changed");
                node.removeAttribute("newfile");

                var page = tabEditors.getPage("/workspace/.c9.test.pattern");
                tabEditors.remove(page);

                ide.removeEventListener("beforefilesave", arguments.callee);

                return false;
            }
        });
    },

    findParent : function(path){
        var _self = this;
        for (var i = 0; i < _self.submodules.length; i++) {
            if (path.match(new RegExp("^\/workspace\/" + _self.submodules[i].replace(/\//g, "\\\/"))))
                return mdlTests.queryNode("repo[@name='" + _self.submodules[i].replace(/'/g, "\\'") + "']");
        }

        return mdlTests.queryNode("repo[1]");
    },

    filter : function(value){
        this.appliedFilter = value;

        dgTestProject.setAttribute("each", value == "all"
            ? "[repo|file|test|assert|error]"
            : "[repo|file[@type='" + value + "']|test|assert|error]");
    },

    parseFile : function(xmlNode){
        ide.dispatchEvent("test.expand." + xmlNode.getAttribute("type"), {
            xmlNode : xmlNode
        });

        return "<file />";
    },

    getIcon : function(tagName, value, type) {
        if (tagName == "repo")
            return "folder.png";
        if (tagName == "assert" || tagName == "error" || tagName == "test") {
            if (!value || value == -1)
                return "bullet_blue.png";
            else if (value == 5) //running
                return "bullet_go.png";
            else if (value == 1) //ran
                return "bullet_green.png";
            else if (value == 0) //error
                return "exclamation.png";//bullet_red.png";
        }
        if (tagName == "error")
            return "exclamation.png";
        else
            return ide.dispatchEvent("test.icon." + type) || "page_white_text.png";
    },

    run : function(nodes){
        var _self = this;

        if (!nodes || stTestRun.active)
            return;

        mnuRunSettings.hide();

        var finish = function(){
            stTestRun.deactivate();
        }

        //Clean nodes
        nodes.each(function(node) {
            if (node.tagName == "test")
                node = node.parentNode;

            var cleanNodes = node.selectNodes(".//file|.//test");
            for (var k = 0; k < cleanNodes.length; k++) {
                apf.xmldb.removeAttribute(cleanNodes[k], "status");
            }
            [".//error", ".//assert"].forEach(function(type){
                var nodes = node.selectNodes(type);
                for (var k = 0; k < nodes.length; k++) {
                    apf.xmldb.removeNode(nodes[k]);
                }
            });
        });

        //Expand list
        var total = [];
        nodes.each(function(node){
            if (node.tagName == "repo")
                total = total.concat(apf.getArrayFromNodelist(node.selectNodes("file" +
                    (_self.appliedFilter == "all"
                        ? ""
                        : "[@type='" + _self.appliedFilter + "']"))));
            else if (node.tagName == "file")
                total.push(node);
            else if (node.tagName == "test")
                total.push(node.parentNode);
        });

        stTestRun.activate();

        var i = 0;
        var next = function(){
            if (total[i]) {
                _self.setLog(total[i], "connecting");
                ide.dispatchEvent("test.run." + total[i].getAttribute("type"), {
                    xmlNode : total[i++],
                    next    : next
                });
            }
            else {
                finish();
            }
        };
        next();
    },

    stop : function(immediate){
        if (!stTestRun.active)
            return;

        ide.dispatchEvent("test.stop");
        stTestRun.setAttribute("stopping", 1);

        var _self = this;
        clearTimeout(this.$stopTimer);
        this.$stopTimer = setTimeout(function(){
            ide.dispatchEvent("test.hardstop");

            _self.stopped();
        }, immediate ? 0 : 10000);
    },

    stopped : function(){
        stTestRun.deactivate();
        stTestRun.removeAttribute("stopping");

        clearTimeout(this.$stopTimer);
    },

    setPass : function(xmlNode, msg){
        apf.xmldb.setAttribute(xmlNode, "status", 1);
        apf.xmldb.setAttribute(xmlNode, "status-message", msg || "");
    },
    setError : function(xmlNode, msg){
        apf.xmldb.setAttribute(xmlNode, "status", 0);
        apf.xmldb.setAttribute(xmlNode, "status-message", msg || "");
    },
    setLog : function(xmlNode, msg){
        apf.xmldb.setAttribute(xmlNode, "status", -1);
        apf.xmldb.setAttribute(xmlNode, "status-message", msg || "");
    },
    lastExecuteNode : null,
    setExecute : function(xmlNode, msg){
        if (xmlNode) {
            apf.xmldb.setAttribute(xmlNode, "status", 5);
            apf.xmldb.setAttribute(xmlNode, "status-message", msg || "");

            ide.dispatchEvent("test.pointer." + apf.queryValue(xmlNode, "ancestor-or-self::test/../@type"), {
                xmlNode : xmlNode
            });
        }
        if (this.lastExecuteNode
          && this.lastExecuteNode.getAttribute("status") == 5) {
            apf.xmldb.setAttribute(this.lastExecuteNode, "status", 1);
            apf.xmldb.setAttribute(this.lastExecuteNode, "status-message", "");
        }
        this.lastExecuteNode = xmlNode;
    },

    showSubmodules : true,
    toggleSubmodules : function(value){
        this.showSubmodules = value;

        settings.model.setQueryValue('auto/testpanel/@showlibraries', this.showSubmodules);

        if (value) {
            dgTestProject.setAttribute("each",
                "[" + dgTestProject.each.replace(/repo\[1\]/, "repo") + "]");
        }
        else {
            dgTestProject.setAttribute("each",
                "[" + dgTestProject.each.replace(/repo/, "repo[1]") + "]");
        }
    },

    expandTests : true,
    toggleExpandTests : function(value){
        this.expandTests = value;

        settings.model.setQueryValue('auto/testpanel/@autoexpand', this.expandTests);

        if (value) {
            if (!expTestRule.parentNode)
                dgTestProject.appendChild(expTestRule);
        }
        else {
            if (expTestRule.parentNode)
                dgTestProject.removeChild(expTestRule);
        }
    },

    show : function(){
        if (navbar.current) {
            if (navbar.current == this)
                return;
            navbar.current.disable();
        }

        panels.initPanel(this);
        this.enable();
    },

    destroy : function(){
        this.stop();
        panels.unregister(this);
        this.$destroy();
    }
});

});
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var css = require("text!ext/splitview/splitview.css");
var Editors = require("ext/editors/editors");
var Commands = require("ext/commands/commands");
var Tabbehaviors = require("ext/tabbehaviors/tabbehaviors");
var Settings = require("ext/settings/settings");
var Code = require("ext/code/code");

var Splits = require("ext/splitview/splits");

var Editsession = require("ace/edit_session").EditSession;

var mnuCloneView, mnuSplitAlign;

var restoring = false;
var restoreQueue = [];
var SUPPORTED_EDITORS = ["ext/code/code", "ext/terminal/terminal"];

module.exports = ext.register("ext/splitview/splitview", {
    name     : "Split View",
    dev      : "Ajax.org",
    alone    : true,
    type     : ext.GENERAL,
    nodes    : [],

    splits   : [],

    init : function(){
        apf.importCssString(css || "");
        ide.addEventListener("theme.init", function(e) {
            var cssClass = e.theme.cssClass;
            var isDark = e.theme.isDark;
            var stringColor = apf.getStyleRule("." + cssClass + " .ace_string", "color");
            apf.importStylesheet([
                ["." + cssClass + " .editor_tab .btnsesssioncontainer div.curbtn.splitview_active .tab_middle", "color: " + stringColor + " !important"],
                ["." + cssClass + " .editor_tab .btnsesssioncontainer div.curbtn.splitview_active .tab_middle", "text-shadow: " + (isDark
                    ? "rgba(0, 0, 0, .71) !important"
                    : "rgba(255, 255, 255, .71) 0px 1px 0px !important")]
            ]);
        });

        var _self = this;
        var tabs = tabEditors; // localize global 'tabEditors'

        Commands.addCommand({
            name: "mergetableft",
            bindKey : {mac: "Command-Option-Ctrl-[", win: "Ctrl-Alt-Meta-["},
            hint: "Add the page on the left of the currently active page to a split view",
            isAvailable : function(){
                //@todo
                return ide.onLine && tabs.length > 1
                  && mnuContextTabs.$page
                  && mnuContextTabs.$page != tabs.getPage(0);
            },
            exec: function(){
                _self.mergetableft();
            }
        });
        Commands.addCommand({
            name: "mergetabright",
            bindKey : {mac: "Command-Option-Ctrl-]", win: "Ctrl-Alt-Meta-]"},
            hint: "Add the page on the right of the currently active page to a split view",
            isAvailable : function(){
                //@todo
                return ide.onLine && tabs.length > 1
                  && mnuContextTabs.$page
                  && mnuContextTabs.$page.nextSibling
                  && mnuContextTabs.$page.nextSibling.localName == "page";
            },
            exec: function(){
                _self.mergetableft();
            }
        });

        Commands.addCommand({
            name: "nexteditor",
            bindKey : {mac: "Ctrl-]", win: "Ctrl-]"},
            hint: "Navigate to the next editor right or below the editor that is currently active in the current split view",
            isAvailable : function(){
                //@todo
                return true;
            },
            exec: function(){
                _self.mergetableft();
            }
        });
        Commands.addCommand({
            name: "preveditor",
            bindKey : {mac: "Ctrl-[", win: "Ctrl-["},
            hint: "Navigate to the previous editor left or above the editor that is currently active in the current split view",
            isAvailable : function(){
                //@todo
                return true;
            },
            exec: function(){
                _self.mergetableft();
            }
        });

        //@todo add menus

        var parent = Tabbehaviors.menu;

        //@todo use menus

        this.nodes.push(
            parent.appendChild(new apf.divider()),
            parent.appendChild(
                //@todo turn into command
                (mnuCloneView = new apf.item({
                    caption : "Clone Editor",
                    type    : "check",
                    checked : false,
                    isAvailable : function(){
                        return tabs.contextPage.$editor.path == "ext/code/code";
                    },
                    onclick : function() {
                        if (this.checked)
                            _self.startCloneView(tabs.contextPage);
                        else
                            tabs.contextPage.removeNode();
                    }
                }))
            ),
            parent.appendChild(
                //@todo turn into command
                (mnuSplitAlign = new apf.item({
                    caption : "Align splits Vertically",
                    type    : "check",
                    checked : true,
                    isAvailable : function(){
                        //@todo
                        return true;
                    },
                    onclick : function() {
                        _self.changeLayout(tabs.contextPage, this.checked ? "3rows" : "3cols");
                    }
                }))
            )
        );

        ide.addEventListener("tab.beforeswitch", function(e) {
            // the return value actually does something!
            var cancelSwitch = _self.updateSplitView(e.previousPage, e.nextPage);
            var split = Splits.get(e.nextPage)[0];
            if (split)
                Splits.update(split);

            if (cancelSwitch) {
                var page       = e.nextPage;
                var editorPage = tabs.getPage(page.type);

                if (editorPage.model != page.$model)
                    editorPage.setAttribute("model", page.$model);
                if (editorPage.actiontracker != page.$at)
                    editorPage.setAttribute("actiontracker", page.$at);
            }

            return cancelSwitch;
        });

        ide.addEventListener("tab.afterswitch", function(e) {
            var page = e.nextPage;
            if (!Splits.getActive())
                return;
            _self.save();

            if (typeof mnuSyntax == "undefined")
                return;

            var item;
            var syntax = mnuSyntax;
            var value = Code.getContentType(page.$model.data);
            for (var i = 0, l = syntax.childNodes.length; i < l; ++i) {
                item = syntax.childNodes[i];
                if (!item || !item.localName || item.localName != "item")
                    continue;
                if (item.value == value) {
                    item.select();
                    break;
                }
            }
        });

        ide.addEventListener("tab.close", function(e) {
            if (!e.page.$clone)
                return;

            // make sure that the model and actiontracker are NOT cleared when a
            // cloned tab page is closed, otherwise the original editor will be blank
            delete e.page.$at;
            delete e.page.$editor;
        });

        ide.addEventListener("closefile", function(e) {
            _self.onFileClose(e);
        });

        ide.addEventListener("beforecycletab", function(e) {
            _self.onCycleTab(e);
        });

        function onAccessTabbing(e) {
            var split = Splits.get(e.page)[0];
            return !Splits.isActive(split);
        }

        ide.addEventListener("beforenexttab", function(e) {
            return onAccessTabbing(e);
        });

        ide.addEventListener("beforeprevioustab", function(e) {
            return onAccessTabbing(e);
        });

        ide.addEventListener("beforeclosetab", function(e) {
            var split = Splits.get(e.page)[0];
            if (!Splits.isActive(split))
                return;
            e.returnValue = split.pairs[split.activePage].page;
        });

        ide.addEventListener("correctactivepage", function(e) {
            var split = Splits.getActive();
            var editor = Editors.currentEditor && Editors.currentEditor.amlEditor;
            if (!split || !editor)
                return;
            var idx = Splits.indexOf(split, editor);
            if (idx == -1)
                return;
            e.returnValue = split.pairs[idx].page;
        });

        tabs.addEventListener("tabselectclick", function(e) {
            return _self.onTabClick(e.page, e.htmlEvent.shiftKey);
        });

        tabs.addEventListener("tabselectmouseup", function(e) {
            var page = this.$activepage;
            var split = Splits.get(page)[0];
            if (split)
                Splits.update(split);
        });

        tabs.addEventListener("reorder", function(e) {
            Splits.get(e.page).forEach(function(split) {
                if (Splits.isActive(split))
                    Splits.update(split);
            });
            _self.save();
        });

        ide.addEventListener("settings.load", function(e) {
            if (!e.model || !e.model.data)
                return;
            var data = e.model.data;
            ide.addEventListener("extload", function(){
                setTimeout(function() {
                    _self.restore(data);
                });
            });
        });

        ide.addEventListener("tab.create", function(e) {
            var page = e.page;
            var xmlNode = e.doc.getNode();
            if (!apf.isTrue(xmlNode.getAttribute("clone")))
                return;

            var id = page.id;
            var pages = tabs.getPages();
            var origPage;
            // loop to find 2nd tab.
            for (var i = 0, l = pages.length; i < l; ++i) {
                if (pages[i] !== page && pages[i].id == id) {
                    origPage = pages[i];
                    break;
                }
            }

            // if 2nd page found, join em!
            if (!origPage)
                return;

            var origEditor = origPage.$editor.amlEditor;
            Splits.consolidateEditorSession(origPage, origEditor);
            page.$doc = origPage.$doc;
            page.setAttribute("actiontracker", origPage.$at);
            page.$at = origPage.$at;

            // find the settings node that corresponds with the clone view
            // that is being constructed right now
            var settings, indices, idx;
            if (Settings.model.data) {
                var nodes = Settings.model.data.selectNodes("splits/split");
                for (i = 0, l = nodes.length; i < l; ++i) {
                    pages = nodes[i] && nodes[i].getAttribute("pages");
                    if (!pages)
                        continue;
                    pages = pages.split(",");
                    indices = [];
                    idx = pages.indexOf(origPage.id);
                    while (idx != -1) {
                        indices.push(idx);
                        idx = pages.indexOf(origPage.id, idx + 1);
                    }
                    if (indices.length < 2)
                        continue;
                    settings = nodes[i];
                    break;
                }
            }
            if (settings && apf.isTrue(settings.getAttribute("active")))
                tabs.set(origPage);

            if (!page.$doc.acedoc)
                page.$doc.addEventListener("init", cont);
            else
                cont();

            function cont() {
                var editor = Splits.getCloneEditor(page);

                page.acesession = new Editsession(page.$doc.acedoc);
                page.acesession.setUndoManager(Splits.CloneUndoManager);

                page.$doc.addEventListener("prop.value", function(e) {
                    page.acesession.setValue(e.value || "");
                    editor.$editor.moveCursorTo(0, 0);
                });

                editor.setProperty("value", page.acesession);

                var split = Splits.create(origPage);
                split.clone = true;
                split.pairs.push({
                    page: page,
                    editor: editor
                });

                page.$clone = true;

                if (settings) {
                    var addPage;
                    for (i = 0, l = pages.length; i < l; ++i) {
                        if (pages[i] == origPage.id)
                            continue;
                        addPage = tabs.getPage(pages[i]);
                        if (!addPage || Splits.indexOf(split, addPage) > -1)
                            continue;
                        split.pairs.splice(i, 0, {
                            page: addPage,
                            editor: Splits.getEditor(split, addPage)
                        });
                    }
                    split.activePage = parseInt(settings.getAttribute("activepage"), 10) || 0;
                    split.gridLayout = settings.getAttribute("layout");
                }

                function syntaxListener(e) {
                    editor.setAttribute("syntax", e.value);
                }
                origEditor.addEventListener("prop.syntax", syntaxListener);
                editor.setAttribute("syntax", origEditor.syntax);

                Splits.consolidateEditorSession(page, editor);

                page.addEventListener("DOMNodeRemovedFromDocument", function() {
                    editor.removeEventListener("prop.syntax", syntaxListener);
                    _self.endCloneView(page);
                });

                origPage.addEventListener("DOMNodeRemovedFromDocument", function() {
                    tabs.remove(page, null, true);
                    _self.endCloneView(origPage);
                });

                if (restoreQueue.length) {
                    _self.restore(restoreQueue);
                }
                else {
                    Splits.show(split);
                    mnuCloneView.setAttribute("checked", true);
                    _self.save();
                }
            }
        });

        Splits.init(this);
    },

    mergetableft: function() {
        return this.mergeTab("left");
    },

    mergetabright: function() {
        return this.mergeTab("right");
    },

    mergeTab: function(dir) {
        var bRight   = dir == "right";
        var tabs     = tabEditors;
        var pages    = tabs.getPages();
        var curr     = tabs.getPage();
        var split    = Splits.getActive();
        var splitLen = split ? split.pairs.length : 0;
        if (split && Splits.indexOf(split, curr) > -1)
            curr = split.pairs[bRight ? splitLen - 1 : 0].page;
        if (!curr || pages.length == 1)
            return;

        var idx;
        if (splitLen == 3) {
            // if the max amount of tabs has been reached inside a split view,
            // then the user may remove the last or first tab from it.
            idx = pages.indexOf(split.pairs[bRight ? splitLen - 1 : 0].page);
        }
        else {
            var currIdx = pages.indexOf(curr);
            idx = currIdx + (bRight ? 1 : -1);
        }
        if (idx < 0 || idx > pages.length - 1)
            return;

        // enable split view ONLY for code editors for now...
        if (!this.isSupportedEditor(curr.$editor, pages[idx].$editor))
            return;
        // pass in null to mutate the active split view
        Splits.mutate(null, pages[idx]);
        Splits.update(Splits.getActive());
        this.save();
        return false;
    },

    nexteditor: function() {
        this.cycleEditors("next");
    },

    preveditor: function() {
        this.cycleEditors("prev");
    },

    cycleEditors: function(dir) {
        var split = Splits.getActive();
        if (!split)
            return;

        var bNext   = dir == "next";
        var currIdx = split.activePage;
        var idx     = currIdx + (bNext ? 1 : -1);
        if (idx < 0)
            idx = split.pairs.length - 1;
        if (idx > split.pairs.length - 1)
            idx = 0;

        Splits.setActivePage(split, split.pairs[idx].page);
        return false;
    },

    /**
     * Invoked when a file is closed
     *
     * @param {AmlEvent} e
     */
    onFileClose: function(e) {
        var page = e.page;
        var splits = Splits.get(page);
        for (var i = 0, l = splits.length; i < l; ++i)
            Splits.mutate(splits[i], page);
        this.save();
    },

    /**
     * Invoked when a tab is clicked, that is the part of the tab-button that is NOT
     * a close button.
     *
     * @param {AmlEvent} e
     */
    onTabClick: function(page, shiftKey) {
        var tabs = tabEditors;
        var activePage = tabs.getPage();

        var i, l;
        var ret = null;
        var split = Splits.get(activePage)[0];

        if (split && !shiftKey) {
            for (i = 0, l = split.pairs.length; i < l; ++i) {
                if (split.pairs[i].page !== activePage)
                    continue;
                ret = false;
                break;
            }
            Splits.setActivePage(split, page);
            // only the first tab in the split view is the trigger to select all
            // other tabs as well (because only the page of the first tab is
            // REALLY shown)
            if (ret !== false && page !== split.pairs[0].page) {
                tabs.set(split.pairs[0].page);
                return false;
            }

            return true;
        }
        else if (shiftKey) {
            // enable split view ONLY for code editors for now...
            if (!this.isSupportedEditor(activePage.$editor, page.$editor))
                return;
            // tabs can be merged into and unmerged from a splitview by clicking a
            // tab while holding shift
            //console.log("is clone?",apf.isTrue(page.$doc.getNode().getAttribute("clone")));
            if (split && apf.isTrue(page.$doc.getNode().getAttribute("clone"))) {
                // close the page that contains the clone editor
                var cloneEditor = Splits.getCloneEditor(page);
                for (i = 0, l = split.pairs.length; i < l; ++i) {
                    if (split.pairs[i].editor !== cloneEditor)
                        continue;
                    tabs.remove(split.pairs[i].page, null, true);
                    return false;
                }
            }
            else {
                ret = !Splits.mutate(split, page);
                this.save();
            }
            return ret;
        }
    },

    /**
     * Tab cycling is handled by the tabbehaviors extension, which emits an event
     * we can hook into. We correct the tab to switch to if a user lands onto a
     * split view while cycling.
     *
     * @param {AmlEvent} e
     */
    onCycleTab: function(e) {
        var pages  = e.pages;
        var split = Splits.getActive();
        if (!split)
            return;
        if (split.pairs.length == pages.length)
            return (e.returnValue = false);

        var maxIdx = pages.length - 1;
        var bRight = e.dir == "right";
        var idx = pages.indexOf(split.pairs[bRight ? split.pairs.length - 1 : 0].page) + (bRight ? 1 : -1);
        idx = idx < 0 ? maxIdx : idx > maxIdx ? 0 : idx;
        if (Splits.indexOf(split, pages[idx]) > -1)
            return (e.returnValue = false);

        // check if the next tab is inside a split as well:
        split = Splits.get(pages[idx])[0];
        if (split)
            e.returnValue = pages.indexOf(split.pairs[0].page);
        else
            e.returnValue = idx;
    },

    updateSplitView: function(previous, next) {
        var editor;
        var doc = next.$doc;
        var at  = next.$at;
        // check if this is a valid clone session
        var split = Splits.get(next)[0];

        // hide the previous split view
        if (previous && previous.$model) {
            var oldSplit = Splits.get(previous)[0];
            if (oldSplit && (!split || oldSplit.gridLayout != split.gridLayout))
                Splits.hide(oldSplit);
        }

        // enable split view ONLY for code editors for now...
        if (this.isSupportedEditor(next.$editor)) {
            mnuCloneView.enable();
            mnuSplitAlign.enable();
        }
        else {
            mnuCloneView.disable();
            mnuSplitAlign.disable();
        }

        mnuCloneView.setAttribute("checked", false);
        mnuSplitAlign.setAttribute("checked", false);

        // all this must exist
        if (!doc || !at || !split) {
            // if it doesn't, make sure the editor is visible and correctly displayed
            editor = next.$editor.amlEditor;
            if (!editor)
                return;
            Splits.consolidateEditorSession(next, editor);
            var nextPage = next.fake ? next.relPage : next;
            if (editor.parentNode != nextPage)
                nextPage.appendChild(editor);
            editor.show();
            return;
        }

        Splits.show(split);
        mnuSplitAlign.setAttribute("checked", split.gridLayout == "3rows");

        if (split.clone)
            mnuCloneView.setAttribute("checked", true);
        else
            mnuCloneView.disable();

        apf.layout.forceResize();

        this.save();

        return false;
    },

    changeLayout: function(page, gridLayout) {
        var split = Splits.get(page)[0];
        if (!split || split.gridLayout == gridLayout)
            return;

        Splits.update(split, gridLayout);
        mnuSplitAlign.setAttribute("checked", gridLayout == "3rows");
        this.save();
    },

    /**
     *
     */
    startCloneView: function(page) {
        var split = this.getCloneView(page);
        var doc  = page.$doc;

        if (split || !doc || !Splits.getEditorSession(page))
            return;

        apf.xmldb.setAttribute(doc.getNode(), "clone", true);
        Editors.openEditor(doc, false, false, true);
    },

    endCloneView: function(page) {
        mnuCloneView.setAttribute("checked", false);
        var split = this.getCloneView(page);
        if (!split)
            return;

        delete split.clone;
        apf.xmldb.setAttribute(page.$doc.getNode(), "clone", false);
    },

    getCloneView: function(page) {
        //@todo this can never work. Local variable...
        var splits = Splits.get(page);
        if (!splits.length)
            return null;

        for (var i = 0, l = splits.length; i < l; ++i) {
            if (splits[i] && splits[i].clone)
                return splits[i];
        }
        return null;
    },

    save: function() {
        if (!Settings.model || restoring)
            return;

        var node = apf.createNodeFromXpath(Settings.model.data, "splits");
        var i, l;
        for (i = node.childNodes.length - 1; i >= 0; --i)
            node.removeChild(node.childNodes[i]);

        var splits = Splits.get();
        var splitEl;
        for (i = 0, l = splits.length; i < l; ++i) {
            splitEl = apf.getXml("<split />");
            splitEl.setAttribute("pages", splits[i].pairs.map(function(pair) {
                return pair.page.id;
            }).join(","));
            splitEl.setAttribute("active", Splits.isActive(splits[i]) ? "true" : "false");
            splitEl.setAttribute("activepage", splits[i].activePage + "");
            splitEl.setAttribute("layout", splits[i].gridLayout);
            node.appendChild(splitEl);
        }
        apf.xmldb.applyChanges("synchronize", node);
    },

    restore: function(settings) {
        // no tabs open... don't bother ;)
        var tabs = tabEditors;
        if (tabs.getPages().length <= 1)
            return;

        var nodes;
        var splits = Splits.get();
        if (apf.isArray(settings))
            nodes = settings;
        else
            nodes = settings.selectNodes("splits/split");

        if (!nodes || !nodes.length)
            return;

        restoring = true;
        var activePage = ide.getActivePage();

        var node, ids, j, l2, id, dupes, hasClone, split, page, editor, active;
        for (var i = nodes.length - 1; i >= 0; --i) {
            node = nodes.pop();
            ids = node.getAttribute("pages").split(",");

            hasClone = false;
            dupes = {};
            for (j = 0, l2 = ids.length; j < l2; ++j) {
                id = ids[j];
                if (!dupes[id]) {
                    dupes[id] = 1;
                }
                else {
                    dupes[id]++;
                    hasClone = id;
                }
            }

            ids = Object.keys(dupes);
            l2 = ids.length;
            if (l2 < 2)
                continue;

            if (hasClone) {
                page = tabs.getPage(hasClone);
                if (!page)
                    continue;
                if (!page.$doc.acesession) {
                    if (restoreQueue.indexOf(node) == -1)
                        restoreQueue.push(node);
                    continue;
                }
                else {
                    split = this.getCloneView(page);
                    if (!split) {
                        if (restoreQueue.indexOf(node) == -1)
                            restoreQueue.push(node);
                        continue;
                    }
                }
            }
            else {
                split = {
                    pairs: [],
                    gridLayout: node.getAttribute("layout") || null
                };
            }

            split.activePage = parseInt(node.getAttribute("activepage"), 10);
            if (split.activePage < 0)
                split.activePage = 0;

            for (j = 0; j < l2; ++j) {
                id = ids[j];
                if (id == hasClone)
                    continue;
                page = tabs.getPage(id);
                if (!page || splits.indexOf(split, page) > -1)
                    continue;
                editor = Splits.getEditor(split, page);
                split.pairs.push({
                    page: page,
                    editor: editor
                });
            }

            if (split.pairs.length > 1) {
                if (apf.isTrue(node.getAttribute("active")))
                    active = split;
                if (splits.indexOf(split) == -1)
                    splits.push(split);
            }
        }
        Splits.set(splits);

        if (!active || splits.indexOf(active, activePage) == -1) {
            tabs.set(activePage);
        }
        else if (active) {
            tabs.set(active.pairs[0].page);
            Splits.update(active);
            Splits.show(active);
            mnuSplitAlign.setAttribute("checked", active.gridLayout == "3rows");
            if (active.clone)
                mnuCloneView.setAttribute("checked", true);
        }

        if (!restoreQueue.length)
            restoring = false;
    },

    isSupportedEditor: function() {
        var editor;
        for (var i = 0, l = arguments.length; i < l; ++i) {
            editor = arguments[i];
            if (!editor || SUPPORTED_EDITORS.indexOf(editor.path) === -1)
                return false;
        }
        return true;
    },

    destroy : function(){
        Commands.removeCommandsByName(["mergetableft", "mergetabright"]);
        this.$destroy();
    }
});

});
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var settings = require("ext/settings/settings");
var markupSettings =  require("text!ext/closeconfirmation/settings.xml");

module.exports = ext.register("ext/closeconfirmation/closeconfirmation", {
    name    : "Confirm closing",
    dev     : "Ajax.org",
    alone   : true,
    type    : ext.GENERAL,
    markup  : null,
    
    deps    : [ settings ],
    
    nodes : [],
    
    hook : function () {
        // when unloading the window
        window.onbeforeunload = this.onBeforeUnloadHandler;
 
        require("ext/settings/settings").addSettings("General", markupSettings );
     
        // init extension
        ext.initExtension(this);
    },
    
    init : function () {
    },
    
    onBeforeUnloadHandler : function () {
        var changed = false;
        tabEditors.getPages().forEach(function(page){
            var at = page.$at;
            if (!at.undo_ptr)
                at.undo_ptr = at.$undostack[0];
            var node = page.$doc.getNode();
            if (node && at.undo_ptr && at.$undostack[at.$undostack.length-1] !== at.undo_ptr
              || !at.undo_ptr && node.getAttribute("changed") == 1
              && page.$doc.getValue()) {
                  changed = true;
            }
        });
        
        if(changed)
            return "You have unsaved changes. Your changes will be lost if you don't save them";
            
        // see what's in the settings
        var settingsNode = settings.model.queryNode("general/@confirmexit");
        if (settingsNode && apf.isTrue(settingsNode.value)) {
            return "You're about to leave Cloud9 IDE.";
        }
    },

    enable : function() {
        this.nodes.each(function(item){
            item.enable();
        });
    },
    
    disable : function() {
        this.nodes.each(function(item){
            item.disable();
        });
    },

    destroy : function() {
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        this.nodes = [];
        
        // clean out the event handler
        if (window.onbeforeunload === this.onBeforeUnloadHandler) {
            window.onbeforeunload = null;
        }
    }
});

});
Example #20
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var lang = require("pilot/lang");
var panels = require("ext/panels/panels");
var editors = require("ext/editors/editors");
var Parser = require("ext/console/parser");
var Trie = require("ext/console/trie");
var css = require("text!ext/console/console.css");
var markup = require("text!ext/console/console.xml");

var trieCommands, hintNodes, hintsTimer,
    selectedHint = null,
    commands     = {},
    cmdTries     = {},
    cmdFetched   = false,
    cmdHistory   = [],
    cmdBuffer    = "",
    lastSearch   = null,
    parser       = new Parser();

return ext.register("ext/console/console", {
    name   : "Console",
    dev    : "Ajax.org",
    type   : ext.GENERAL,
    alone  : true,
    markup : markup,
    css    : css,
    
    commandHistoryIndex : 0,
    excludeParent : true,
    commands : {
        "help": {hint: "show general help information and a list of available commands"},
        "clear": {hint: "clear all the messages from the console"},
        "switchconsole" : {hint: "toggle focus between the editor and the console"},
        "send": {hint: "send a message to the server"}
    },

    help : function() {
        var words = trieCommands.getWords(),
            text  = [];

        for (var i = 0, l = words.length; i < l; ++i) {
            if (!commands[words[i]]) continue;
            text.push(words[i] + "\t\t\t\t" + commands[words[i]].hint);
        }
        this.logNodeStream(text.join("\n"));
    },
    
    clear : function() {
        this.inited && txtOutput.clear();
    },
    
    switchconsole : function() {
        if (apf.activeElement == txtConsoleInput) {
            if (window.ceEditor) {
                ceEditor.focus();
                this.disable();
            }
        }
        else
            txtConsoleInput.focus()
    },

    send : function(data) {
        ide.socket.send(data.line.replace(data.command,"").trim());
        return true;
    },

    
    showOutput : function(){
        tabConsole.set(1);
    },

    jump: function(path, row, column) {
        row = parseInt(row.slice(1));
        column = column ? parseInt(column.slice(1)) : 0;
        editors.showFile(path, row, column);
    },

    getCwd: function() {
        return this.$cwd && this.$cwd.replace("/workspace", ide.workspaceDir);
    },

    logNodeStream : function(data, stream, useOutput) {
        var colors = {
            30: "#eee",
            31: "red",
            32: "green",
            33: "yellow",
            34: "blue",
            35: "magenta",
            36: "cyan",
            37: "#eee"
        };

        workspaceDir = ide.workspaceDir;
        davPrefix = ide.davPrefix;

        var lines = data.split("\n");
        var style = "color:#eee;";
        var log = [];
        // absolute workspace files
        var wsRe = new RegExp(lang.escapeRegExp(workspaceDir) + "\\/([^:]*)(:\\d+)(:\\d+)*", "g");
        // relative workspace files
        var wsrRe = /(?:\s|^|\.\/)([\w\_\$-]+(?:\/[\w\_\$-]+)+(?:\.[\w\_\$]+))?(\:\d+)(\:\d+)*/g;

        for (var i=0; i<lines.length; i++) {
            if (!lines[i]) continue;
            log.push("<div class='item'><span style='" + style + "'>" + lines[i]
                .replace(wsRe, "<a href='javascript:void(0)' onclick='require(\"ext/console/console\").jump(\"" + davPrefix + "/$1\", \"$2\", \"$3\")'>"+workspaceDir+"/$1$2$3</a>")
                .replace(wsrRe, "<a href='javascript:void(0)' onclick='require(\"ext/console/console\").jump(\"" + davPrefix + "/$1\", \"$2\", \"$3\")'>$1$2$3</a>")
                .replace(/\s{2,}/g, function(str) {
                    return lang.stringRepeat("&nbsp;", str.length)
                })
                .replace(/(((http:\/\/)|(www\.))[\w\d\.-]*(:\d+)?(\/[\w\d]+)?)/, "<a href='$1' target='_blank'>$1</a>")
                // tty escape sequences (http://ascii-table.com/ansi-escape-sequences.php)
                .replace(/(\u0007|\u001b)\[(K|2J)/g, "")
                .replace(/\033\[(?:(\d+);)?(\d+)m/g, function(m, extra, color) {
                    style = "color:" + (colors[color] || "#eee");
                    if (extra == 1) {
                        style += ";font-weight=bold"
                    } else if (extra == 4) {
                        style += ";text-decoration=underline";
                    }
                    return "</span><span style='" + style + "'>"
                }) + "</span></div>");
        }

        (useOutput ? txtOutput : txtConsole).addValue(log.join(""));
    },

    log : function(msg, type, pre, post, otherOutput){
        msg = apf.htmlentities(String(msg));

        if (!type)
            type = "log";
        else if (type == "divider") {
            msg = "<span style='display:block;border-top:1px solid #444; margin:6px 0 6px 0;'></span>";
        }
        else if (type == "prompt") {
            msg = "<span style='color:#86c2f6'>" + msg + "</span>";
        }
        else if (type == "command") {
            msg = "<span style='color:#86c2f6'><span style='float:left'>&gt;&gt;&gt;</span><div style='margin:0 0 0 25px'>"
                + msg + "</div></span>";
        }
        (otherOutput || txtConsole).addValue("<div class='item console_" + type + "'>" + (pre || "") + msg + (post || "") + "</div>");
    },

    write: function(aLines) {
        if (typeof aLines == "string")
            aLines = aLines.split("\n");
        for (var i = 0, l = aLines.length; i < l; ++i)
            this.log(aLines[i], "log");
        this.log("", "divider");
    },

    evaluate : function(expression, callback){
        var _self = this;
        var frame = dgStack && dgStack.selected && dgStack.selected.getAttribute("ref") || null;
        dbg.evaluate(expression, frame, null, null, callback || function(xmlNode){
            _self.showObject(xmlNode);
        });
    },

    checkChange : function(xmlNode){
        var value = xmlNode.getAttribute("value");
        if (xmlNode.tagName == "method" || "Boolean|String|undefined|null|Number".indexOf(xmlNode.getAttribute("type")) == -1)
            return false;
    },

    applyChange : function(xmlNode){
        var value = xmlNode.getAttribute("value");
        var name = this.calcName(xmlNode);
        try{
            if (name.indexOf(".") > -1) {
                var prop, obj = self.parent.eval(name.replace(/\.([^\.\s]+)$/, ""));
                if (obj && obj.$supportedProperties && obj.$supportedProperties.contains(prop = RegExp.$1)) {
                    obj.setProperty(prop, self.parent.eval(value));
                    return;
                }
            }

            self.parent.eval(name + " = " + value);

            //@todo determine new type
        }
        catch(e) {
            trObject.getActionTracker().undo();
            alert("Invalid Action: " + e.message);
            //@todo undo
        }
    },

    calcName : function(xmlNode, useDisplay){
        var isMethod = xmlNode.tagName == "method";
        var name, loopNode = xmlNode, path = [];
        do {
            name = useDisplay
                ? loopNode.getAttribute("display") || loopNode.getAttribute("name")
                : loopNode.getAttribute("name");

            if (!name)
                break;

            path.unshift(!name.match(/^[a-z_\$][\w_\$]*$/i)
                ? (parseInt(name) == name
                    ? "[" + name + "]"
                    : "[\"" + name.replace(/'/g, "\\'") + "\"]")
                : name);
            loopNode = loopNode.parentNode;
            if (isMethod) {
                loopNode = loopNode.parentNode;
                isMethod = false;
            }
        }
        while (loopNode && loopNode.nodeType == 1);

        if (path[0].charAt(0) == "[")
            path[0] = path[0].substr(2, path[0].length - 4);
        return path.join(".").replace(/\.\[/g, "[");
    },

    keyupHandler: function(e) {
        var code = e.keyCode;
        if (code != 9 && code != 13 && code != 38 && code != 40 && code != 27) {
            return this.commandTextHandler(e);
        }
    },

    keydownHandler: function(e) {
        var code = e.keyCode;
        if (code == 9 || code == 13 || code == 38 || code == 40 || code == 27) {
            return this.commandTextHandler(e);
        }
    },

    commandTextHandler: function(e) {
        var line      = e.currentTarget.getValue(),
            //idx       = cmdHistory.indexOf(line),
            hisLength = cmdHistory.length,
            newVal    = "",
            code      = e.keyCode;
        if (cmdBuffer === null || (this.commandHistoryIndex == 0 && cmdBuffer !== line))
            cmdBuffer = line;
        parser.parseLine(line);

        if (code == 38) { //UP
            if (this.$winHints.visible) {
                this.selectHintUp();
            }
            else {
                if (!hisLength)
                    return;
                newVal = cmdHistory[--this.commandHistoryIndex];
                if (this.commandHistoryIndex < 0)
                    this.commandHistoryIndex = 0;
                if (newVal)
                    e.currentTarget.setValue(newVal);
            }
            return false;
        }
        else if (code == 40) { //DOWN
            if (this.$winHints.visible) {
                this.selectHintDown();
            }
            else {
                if (!hisLength)
                    return;
                newVal = cmdHistory[++this.commandHistoryIndex] || "";//(++idx > hisLength - 1 || idx === 0) ? (cmdBuffer || "") : 
                if (this.commandHistoryIndex >= cmdHistory.length)
                    this.commandHistoryIndex = cmdHistory.length;
                e.currentTarget.setValue(newVal);
            }
            return false;
        }
        else if (code == 27 && this.$winHints.visible) {
            return this.hideHints();
        }
        else if (code != 13 && code != 9) {
            this.autoComplete(e, parser, 2);
            return;
        }
        
        if (this.$winHints.visible && selectedHint && hintNodes)
            return this.hintClick(hintNodes[selectedHint]);

        if (parser.argv.length === 0) {
            // no commmand line input

            if (e.name == "keydown") {
                //this.log(this.getPrompt(), "prompt");
                //this.enable();
            }
        }
        else if (parser.argQL[0]) {
            // first argument quoted -> error
            this.write("Syntax error: first argument quoted.");
        }
        else {
            var s,
                cmd = parser.argv[parser.argc++];

            if (code == 9) {
                this.autoComplete(e, parser, 1);
                return false;
            }

            this.commandHistoryIndex = cmdHistory.push(line);
            cmdBuffer = null;
            e.currentTarget.setValue(newVal);
            this.hideHints();

            this.log(this.getPrompt() + " " + parser.argv.join(" "), "prompt");
            this.enable();
            tabConsole.set("console");

            switch (cmd) {
                case "help":
                    this.help();
                    break;
                case "clear":
                    txtConsole.clear();
                    break;
                case "sudo":
                    s = parser.argv.join(" ").trim();
                    if (s == "sudo make me a sandwich") {
                        this.write("Okay.");
                        break;
                    }
                    else if (s == "sudo apt-get moo") {
                        //this.clear();
                        this.write([" ",
                            "        (__)",
                            "        (oo)",
                            "  /------\\/ ",
                            " / |    ||  ",
                            "*  /\\---/\\  ",
                            "   ~~   ~~  ",
                            "....\"Have you mooed today?\"...",
                            " "]);
                        break;
                    }
                    else {
                        this.write("E: Invalid operation " + parser.argv[parser.argc++]);
                        break;
                    }
                case "man":
                    var pages = {
                        "last": "Man, last night was AWESOME.",
                        "help": "Man, help me out here.",
                        "next": "Request confirmed; you will be reincarnated as a man next.",
                        "cat":  "You are now riding a half-man half-cat."
                    };
                    this.write((pages[parser.argv[parser.argc++]]
                        || "Oh, I'm sure you can figure it out."));
                    break;
                case "locate":
                    var keywords = {
                        "ninja": "Ninja can not be found!",
                        "keys": "Have you checked your coat pocket?",
                        "joke": "Joke found on user.",
                        "problem": "Problem exists between keyboard and chair.",
                        "raptor": "BEHIND YOU!!!"
                    };
                    this.write((keywords[parser.argv[parser.argc++]] || "Locate what?"));
                    break;
                default:
                    var jokes = {
                        "make me a sandwich": "What? Make it yourself.",
                        "make love": "I put on my robe and wizard hat.",
                        "i read the source code": "<3",
                        //"pwd": "You are in a maze of twisty passages, all alike.",
                        "lpr": "PC LOAD LETTER",
                        "hello joshua": "How about a nice game of Global Thermonuclear War?",
                        "xyzzy": "Nothing happens.",
                        "date": "March 32nd",
                        "hello": "Why hello there!",
                        "who": "Doctor Who?",
                        "su": "God mode activated. Remember, with great power comes great ... aw, screw it, go have fun.",
                        "f**k": "I have a headache.",
                        "whoami": "You are Richard Stallman.",
                        "nano": "Seriously? Why don't you just use Notepad.exe? Or MS Paint?",
                        "top": "It's up there --^",
                        "moo":"moo",
                        "ping": "There is another submarine three miles ahead, bearing 225, forty fathoms down.",
                        "find": "What do you want to find? Kitten would be nice.",
                        "more":"Oh, yes! More! More!",
                        "your gay": "Keep your hands off it!",
                        "hi":"Hi.",
                        "echo": "Echo ... echo ... echo ...",
                        "bash": "You bash your head against the wall. It's not very effective.",
                        "ssh": "ssh, this is a library.",
                        "uname": "Illudium Q-36 Explosive Space Modulator",
                        "finger": "Mmmmmm...",
                        "kill": "Terminator deployed to 1984.",
                        "use the force luke": "I believe you mean source.",
                        "use the source luke": "I'm not luke, you're luke!",
                        "serenity": "You can't take the sky from me.",
                        "enable time travel": "TARDIS error: Time Lord missing.",
                        "ed": "You are not a diety."
                    };
                    s = parser.argv.join(" ").trim();
                    if (jokes[s]) {
                        this.write(jokes[s]);
                        break;
                    }
                    else {
                        var data = {
                            command: cmd,
                            argv: parser.argv,
                            line: line,
                            cwd: this.getCwd()
                        };
                        ide.dispatchEvent("track_action", {type: "console", cmd: cmd});
                        if (ext.execCommand(cmd, data) !== false) {
                            if (ide.dispatchEvent("consolecommand." + cmd, {
                              data: data
                            }) !== false) {
                                if (!ide.onLine)
                                    this.write("Cannot execute command. You are currently offline.");
                                else
                                    ide.socket.send(JSON.stringify(data));
                            }
                        }
                        return;
                    }
            }
        }
    },

    onMessage: function(e) {
        var res,
            message = e.message;
            
        if (message.type == "node-data")
            return this.logNodeStream(message.data, message.stream, true);
        
        if (message.type != "result")
            return;

        switch (message.subtype) {
            case "commandhints":
                var cmds = message.body;
                for (var cmd in cmds) {
                    trieCommands.add(cmd);
                    commands[cmd] = cmds[cmd];
                    if (cmds[cmd].commands)
                        this.subCommands(cmds[cmd].commands, cmd);
                }
                cmdFetched = true;
                break;
            case "internal-autocomplete":
                res = message.body;
                var base = res.base || "",
                    blen = base.length;
                lastSearch = res;
                lastSearch.trie = new Trie();
                for (var m, i = 0, l = res.matches.length; i < l; ++i) {
                    m = res.matches[i];
                    lastSearch.trie.add(m);
                    if (base && m.indexOf(base) === 0)
                        res.matches[i] = m.substr(blen - 1);
                }
                this.showHints(res.textbox, res.base || "", res.matches, null, res.cursor);
                break;
            case "cd":
                res = message.body;
                if (res.cwd) {
                    this.$cwd = res.cwd.replace(ide.workspaceDir, "/workspace");
                    this.write("Working directory changed.");
                }
                break;
            case "git":
            case "npm":
            case "pwd":
            case "hg":
            case "ls":
                res = message.body;
                //this.getPrompt() + " " + res.argv.join(" ") + "\n" + 
                if (res.out)
                    this.logNodeStream(res.out);
                if (res.err)
                    this.logNodeStream(res.err);
                if (res.code) // End of command
                    this.log("", "divider");
                break;
            case "mkdir":
                res = message.body;
                ide.dispatchEvent("treecreate", {
                    type : "folder",
                    path : this.$cwd + "/" + res.argv[res.argv.length - 1] 
                });
                break;
            case "error":
                //console.log("error: ", message.body);
                this.log(message.body);
                this.log("", "divider");
                break;
        }

        ide.dispatchEvent("consoleresult." + message.subtype, {data: message.body});
    },

    setPrompt: function(cwd) {
        if (cwd)
            this.$cwd = cwd.replace(ide.workspaceDir, ide.davPrefix);
        return this.getPrompt();
    },

    getPrompt: function() {
        return "[guest@cloud9]:" + this.$cwd + "$";
    },

    subCommands: function(cmds, prefix) {
        if (!cmdTries[prefix]) {
            cmdTries[prefix] = {
                trie    : new Trie(),
                commands: cmds
            };
        }
        for (var cmd in cmds) {
            cmdTries[prefix].trie.add(cmd);
            if (cmds[cmd].commands)
                this.subCommands(cmds[cmd].commands, prefix + "-" + cmd);
        }
    },

    autoComplete: function(e, parser, mode) {
        mode = mode || 2;
        if (mode === 1) {
            if (this.$busy) return;
            var _self = this;
            this.$busy = setTimeout(function(){clearTimeout(_self.$busy);_self.$busy = null;}, 100);
        }
        if (!trieCommands) {
            trieCommands = new Trie();
            apf.extend(commands, ext.commandsLut);
            for (var name in ext.commandsLut) {
                trieCommands.add(name);
                if (ext.commandsLut[name].commands)
                    this.subCommands(ext.commandsLut[name].commands, name);
            }
        }

        // keycodes that invalidate the previous autocomplete:
        if (e.keyCode == 8 || e.keyCode == 46)
            lastSearch = null;

        var root,
            list      = [],
            cmds      = commands,
            textbox   = e.currentTarget,
            val       = textbox.getValue(),
            len       = parser.argv.length,
            base      = parser.argv[0],
            cursorPos = 0;
        if (!apf.hasMsRangeObject) {
            var input = textbox.$ext.getElementsByTagName("input")[0];
            cursorPos = input.selectionStart;
        }
        else {
            var range = document.selection.createRange(),
                r2    = range.duplicate();
            r2.expand("textedit");
            r2.setEndPoint("EndToStart", range);
            cursorPos = r2.text.length;
        }
        --cursorPos;

        if (!cmdFetched) {
            ide.socket.send(JSON.stringify({
                command: "commandhints",
                argv: parser.argv,
                cwd: this.getCwd()
            }));
        }

        if (typeof parser.argv[0] != "string")
            return this.hideHints();

        // check for commands in first argument when there is only one argument
        // provided, or when the cursor on the first argument
        if (len == 1 && cursorPos < parser.argv[0].length) {
            root = trieCommands.find(parser.argv[0]);
            if (root)
                list = root.getWords();
        }
        else {
            var idx, needle, cmdTrie,
                i = len - 1;
            for (; i >= 0; --i) {
                idx = val.indexOf(parser.argv[i]);
                if (idx === -1) //shouldn't happen, but yeah...
                    continue;
                if (cursorPos >= idx || cursorPos <= idx + parser.argv[i].length) {
                    needle = i;
                    break;
                }
            }
            if (typeof needle != "number")
                needle = 0;

            ++needle;
            while (needle >= 0 && !(cmdTrie = cmdTries[parser.argv.slice(0, needle).join("-")]))
                --needle

            if (cmdTrie) {
                //console.log("needle we're left with: ", needle, len, parser.argv[needle]);
                base = parser.argv[needle];
                root = cmdTrie.trie.find(base);
                if (root) {
                    list = root.getWords();
                }
                else {
                    list = cmdTrie.trie.getWords();
                    // check for file/folder autocompletion:
                    if (list.length == 1 && list[0] == "[PATH]") {
                        //console.log("we have PATH, ", base, lastSearch);
                        if (base && lastSearch) {
                            list.splice(0, 1);
                            var newbase = base.split("/").pop();
                            if (!newbase) {
                                base = "";
                                list = lastSearch.trie.getWords();
                            }
                            else if (newbase.indexOf(lastSearch.base) > -1) {
                                // console.log("searching for ", newbase, base, "mode:", mode);
                                root = lastSearch.trie.find(newbase);
                                if (root) {
                                    // console.log("setting base ", base, "to", base, newbase);
                                    base = newbase;
                                    list = root.getWords();
                                }
                            }
                            if (!list.length) {
                                // we COULD do something special here...
                            }
                        }
                        else {
                            if (mode == 2)
                                list.splice(0, 1);
                            //base = "";
                        }
                        // adjust the argv array to match the current cursor position:
                        parser.argv = parser.argv.slice(0, needle);
                        parser.argv.push(base);
                        // else: autocompletion is sent to the backend
                        //console.log("directory found: ", base, list, "mode:", mode);
                    }
                    else {
                        base = "";
                    }
                }
                cmds = cmdTrie.commands;
                //console.log("list: ", list, base, parser.argv);
            }
        }

        if (list.length === 0)
            return this.hideHints();

        if (mode === 2) { // hints box
            this.showHints(textbox, base || "", list, cmds, cursorPos);
        }
        else if (mode === 1) { // TAB autocompletion
            var ins = base ? list[0].substr(1) : list[0];
            if (ins.indexOf("PATH]") != -1 && lastSearch && lastSearch.line == val && lastSearch.matches.length == 1)
                ins = lastSearch.matches[0].replace(lastSearch.base, "");
            if (ins.indexOf("PATH]") != -1) {
                ide.socket.send(JSON.stringify({
                    command: "internal-autocomplete",
                    line   : val,
                    textbox: textbox.id,
                    cursor : cursorPos,
                    argv   : parser.argv,
                    cwd    : this.getCwd()
                }));
            }
            else {
                if (!!(cmds || commands)[base + ins])
                    ins += " "; // for commands we suffix with whitespace
                var newval = val.substr(0, cursorPos + 1) + ins + val.substr(cursorPos + 1);
                if (val != newval)
                    textbox.setValue(newval);
                lastSearch = null;
            }
            this.hideHints();
        }
    },
    
    initHints: function() {
        if (this.$winHints) return;
        this.$winHints = document.getElementById("barConsoleHints");
        apf.addListener(this.$winHints, "mousemove", this.hintsMouseHandler.bind(this));
    },

    showHints: function(textbox, base, hints, cmdsLut, cursorPos) {
        var name = "console_hints";
        if (typeof textbox == "string")
            textbox = self[textbox];
        //console.log("showing hints for ", base, hints && hints[0]);
        //base = base.substr(0, base.length - 1);

        if (this.control && this.control.stop)
            this.control.stop();

        var cmdName, cmd, isCmd,
            content = [],
            i       = 0,
            len     = hints.length;

        for (; i < len; ++i) {
            cmdName = base ? base + hints[i].substr(1) : hints[i];
            //console.log("isn't this OK? ", cmdName, base);
            cmd = (cmdsLut || commands)[cmdName];
            isCmd = !!cmd;
            content.push('<a href="javascript:void(0);" onclick="require(\'ext/console/console\').hintClick(this);" ' 
                + 'data-hint="'+ base + ',' + cmdName + ',' + textbox.id + ',' + cursorPos + ',' + isCmd + '">'
                + cmdName + ( cmd
                ? '<span>' + cmd.hint + (cmd.hotkey
                    ? '<span class="hints_hotkey">' + (apf.isMac
                        ? apf.hotkeys.toMacNotation(cmd.hotkey)
                        : cmd.hotkey) + '</span>'
                    : '') + '</span>'
                : '')
                + '</a>');
        }

        this.initHints();
        hintNodes = null;
        selectedHint = null;

        this.$winHints.innerHTML = content.join("");

        if (apf.getStyle(this.$winHints, "display") == "none") {
            //this.$winHints.style.top = "-30px";
            this.$winHints.style.display = "block";
            this.$winHints.visible = true;
            //txtConsoleInput.focus();

            //Animate
            /*apf.tween.single(this.$winHints, {
                type     : "fade",
                anim     : apf.tween.easeInOutCubic,
                from     : 0,
                to       : 100,
                steps    : 8,
                interval : 10,
                control  : (this.control = {})
            });*/
        }

        var pos = apf.getAbsolutePosition(textbox.$ext, this.$winHints.parentNode);
        this.$winHints.style.left = Math.max(cursorPos * 5.5, 5) + "px";
        //this.$winHints.style.top = (pos[1] - this.$winHints.offsetHeight) + "px";
    },

    hideHints: function() {
        this.initHints();
        this.$winHints.style.display = "none";
        this.$winHints.visible = false;
        selectedHint = null;
        //@todo: animation
    },

    hintClick: function(node) {
        var parts       = node.getAttribute("data-hint").split(","),
            base        = parts[0],
            cmdName     = parts[1],
            txtId       = parts[2],
            insertPoint = parseInt(parts[3]),
            isCmd       = (parts[4] == "true");

        if (isCmd)
            cmdName += " "; // for commands we suffix with whitespace
        var textbox = self[txtId],
            input   = textbox.$ext.getElementsByTagName("input")[0],
            val     = textbox.getValue(),
            before  = val.substr(0, (insertPoint + 1 - base.length)) + cmdName;
        textbox.setValue(before + val.substr(insertPoint + 1));
        textbox.focus();
        // set cursor position at the end of the text just inserted:
        var pos = before.length;
        if (apf.hasMsRangeObject) {
            var range = input.createTextRange();
            range.expand("textedit");
            range.select();
            range.collapse();
            range.moveStart("character", pos);
            range.moveEnd("character", 0);
            range.collapse();
        }
        else {
            input.selectionStart = input.selectionEnd = pos;
        }

        this.hideHints();
    },
    
    hintsMouseHandler: function(e) {
        clearTimeout(hintsTimer);
        var el = e.target || e.srcElement;
        while (el && el.nodeType == 3 && el.tagName != "A" && el != this.$winHints)
            el = el.parentNode;
        if (el.tagName != "A") return;
        var _self = this;
        hintsTimer = setTimeout(function() {
            _self.selectHint(el);
        }, 5);
    },

    selectHintUp: function() {
        if (!hintNodes)
            hintNodes = this.$winHints.getElementsByTagName("a");
        return this.selectHint(selectedHint - 1 < 0 ? hintNodes.length - 1 : --selectedHint);
    },

    selectHintDown: function() {
        if (!hintNodes)
            hintNodes = this.$winHints.getElementsByTagName("a");
        return this.selectHint(selectedHint + 1 > hintNodes.length - 1 ? 0 : ++selectedHint);
    },
    
    selectHint: function(hint) {
        clearTimeout(hintsTimer);
        if (!hintNodes)
            hintNodes = this.$winHints.getElementsByTagName("a");

        if (typeof hint == "number")
            hint = hintNodes[hint];

        for (var i = 0, l = hintNodes.length; i < l; ++i) {
            if (hintNodes[i] === hint) {
                selectedHint = i;
                continue;
            }
            hintNodes[i].className = "";
        }
        if (hint)
            hint.className = "selected";
    },

    consoleTextHandler: function(e) {
        if(e.keyCode == 13 && e.ctrlKey) {
            var _self = this;
            
            var expression = txtCode.getValue().trim();
            if (!expression)
                return;
            
            tabConsole.set(1);
            this.log(expression, "command", null, null, txtOutput);
            
            this.evaluate(expression, function(xmlNode, body, refs, error){
                if (error)
                    _self.log(error.message, "error");
                else {
                    var type = body.type, value = body.value || body.text, ref = body.handle, className = body.className;
                    if (className == "Function") {
                        var pre = "<a class='xmlhl' href='javascript:void(0)' style='font-weight:bold;font-size:7pt;color:green' onclick='require(\"ext/console/console\").showObject(null, ["
                            + body.scriptId + ", " + body.line + ", " + body.position + ", "
                            + body.handle + ",\"" + (body.name || body.inferredName) + "\"], \""
                            + (expression || "").split(";").pop().replace(/"/g, "\\&quot;") + "\")'>";
                        var post = "</a>";
                        var name = body.name || body.inferredName || "function";
                        _self.log(name + "()", "log", pre, post, txtOutput);
                    }
                    else if (className == "Array") {
                        var pre = "<a class='xmlhl' href='javascript:void(0)' style='font-weight:bold;font-size:7pt;color:green' onclick='require(\"ext/console/console\").showObject(\""
                            + apf.escapeXML(xmlNode.xml.replace(/"/g, "\\\"")) + "\", "
                            + ref + ", \"" + apf.escapeXML((expression || "").trim().split(/;|\n/).pop().trim().replace(/"/g, "\\\"")) + "\")'>";
                        var post = " }</a>";

                        _self.log("Array { length: "
                            + (body.properties && body.properties.length - 1), "log", pre, post, txtOutput);
                    }
                    else if (type == "object") {
                        var refs = [], props = body.properties;
                        for (var i = 0, l = body.properties.length; i < l; i++) {
                            refs.push(props[i].ref);
                        }

                        var pre = "<a class='xmlhl' href='javascript:void(0)' style='font-weight:bold;font-size:7pt;color:green' onclick='require(\"ext/console/console\").showObject(\""
                            + apf.escapeXML(xmlNode.xml.replace(/"/g, "\\\"")) + "\", "
                            + ref + ", \"" + apf.escapeXML((expression || "").trim().split(/;|\n/).pop().trim().replace(/"/g, "\\\"")) + "\")'>";
                        var post = " }</a>";

                        dbg.$debugger.$debugger.lookup(refs, false, function(body) {
                            var out = [className || value, "{"];
                            for (var item, t = 0, i = 0; i < l; i++) {
                                item = body[refs[i]];
                                if (item.className == "Function" || item.className == "Object")
                                    continue;
                                if (t == 5) {
                                    out.push("more...");
                                    break;
                                }
                                var name = props[i].name || (props[i].inferredName || "Unknown").split(".").pop();
                                out.push(name + "=" + item.value, ", ");
                                t++;
                            }
                            if (t) out.pop();

                            _self.log(out.join(" "), "log", pre, post, txtOutput);
                        });
                    }
                    else
                        _self.log(value, "log", null, null, txtOutput);
                }
            });

            require("ext/settings/settings").save();
            return false;
        }
    },

    showObject : function(xmlNode, ref, expression) {
        if (ref && ref.dataType == apf.ARRAY) {
            require(["ext/debugger/debugger"], function(dbg) {
                dbg.showDebugFile(ref[0], ref[1] + 1, 0, ref[4]);
            });
        }
        else {
            require(["ext/quickwatch/quickwatch"], function(quickwatch) {
                quickwatch.toggleDialog(1);
                
                if (xmlNode && typeof xmlNode == "string")
                    xmlNode = apf.getXml(xmlNode);

                var name = xmlNode && xmlNode.getAttribute("name") || expression;
                txtCurObject.setValue(name);
                dgWatch.clear("loading");

                if (xmlNode) {
                    setTimeout(function(){
                        var model = dgWatch.getModel();
                        var root  = apf.getXml("<data />");
                        apf.xmldb.appendChild(root, xmlNode);
                        model.load(root);
                        //model.appendXml(xmlNode);
                    }, 10);
                }
                else if (ref) {

                }
                else {
                    this.evaluate(expression);
                }
            });

        }
    },

    types : ["Object", "Number", "Boolean", "String", "Array", "Date", "RegExp", "Function", "Object"],
    domtypes : [null, "Element", "Attr", "Text", "CDataSection",
                "EntityReference", "Entity", "ProcessingInstruction", "Comment",
                "Document", "DocumentType", "DocumentFragment", "Notation"],

    calcName : function(xmlNode, useDisplay){
        var isMethod = xmlNode.tagName == "method";
        var name, loopNode = xmlNode, path = [];
        do {
            name = useDisplay
                ? loopNode.getAttribute("display") || loopNode.getAttribute("name")
                : loopNode.getAttribute("name");

            if (!name)
                break;

            path.unshift(!name.match(/^[a-z_\$][\w_\$]*$/i)
                ? (parseInt(name) == name
                    ? "[" + name + "]"
                    : "[\"" + name.replace(/'/g, "\\'") + "\"]")
                : name);
            loopNode = loopNode.parentNode;
            if (isMethod) {
                loopNode = loopNode.parentNode;
                isMethod = false;
            }
        }
        while (loopNode && loopNode.nodeType == 1);

        if (path[0].charAt(0) == "[")
            path[0] = path[0].substr(2, path[0].length - 4);
        return path.join(".").replace(/\.\[/g, "[");
    },

    /**** Init ****/

    hook : function(){
        panels.register(this);
        panels.initPanel(this);
    },

    init : function(amlNode){
        var _self = this
        this.panel = tabConsole;
        this.$cwd  = "/workspace";

        //Append the console window at the bottom below the tab
        mainRow.appendChild(winDbgConsole); //selectSingleNode("a:hbox[1]/a:vbox[2]").

        apf.importCssString((this.css || "") + " .console_date{display:inline}");
        
        stProcessRunning.addEventListener("activate", function() {
            _self.clear();
            _self.showOutput();
            _self.enable();
        });
        
        ide.addEventListener("socketMessage", this.onMessage.bind(this));
        ide.addEventListener("consoleresult.internal-isfile", function(e) {
            var data = e.data;
            var path = data.cwd.replace(ide.workspaceDir, ide.davPrefix);
            if (data.isfile)
                editors.showFile(path);
            else
                _self.log("'" + path + "' is not a file.");
        });
        
        winDbgConsole.previousSibling.hide();
    },

    enable : function(fromParent){
        /*if (!this.panel)
            panels.initPanel(this);

        if (this.manual && fromParent)
            return;

        if (!fromParent)
            this.manual = true;*/

        this.mnuItem.check();
        tabConsole.show();

        if (winDbgConsole.height == 41)
            winDbgConsole.setAttribute("height", this.height || 200);
        winDbgConsole.previousSibling.show();
        
        apf.layout.forceResize();
        apf.setStyleClass(btnCollapseConsole.$ext, "btn_console_openOpen");

    },

    disable : function(fromParent){
        /*if (this.manual && fromParent || !this.inited)
            return;

        if (!fromParent)
            this.manual = true;*/

        this.mnuItem.uncheck();
        tabConsole.hide();

        if (winDbgConsole.height != 41)
            this.height = winDbgConsole.height;
        winDbgConsole.setAttribute("height", 41);
        winDbgConsole.previousSibling.hide();
        
        apf.layout.forceResize();
        apf.setStyleClass(btnCollapseConsole.$ext, '' , ['btn_console_openOpen']);
    },

    destroy : function(){
        winDbgConsole.destroy(true, true);
        panels.unregister(this);
    }
});

});
Example #21
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var commands = require("ext/commands/commands");

module.exports = ext.register("ext/filesystem/filesystem", {
    name   : "File System",
    dev    : "Ajax.org",
    type   : ext.GENERAL,
    alone  : true,
    deps   : [],

    readFile : function (path, callback){
        if (!this.webdav) return;
        
        var self = this;
        
        // in webdav.read, if ide.onLine === 0, it is calling callback immediately without content
        // if we're not online, we'll add an event handler that listens to the socket connecting (or the ping or so)
        if (!ide.onLine) {
            var afterOnlineHandler = function () {
                self.webdav.read(path, callback);
                ide.removeEventListener("afteronline", afterOnlineHandler);
            };
            ide.addEventListener("afteronline", afterOnlineHandler);
        }
        else {
            // otherwise just redirect it
            this.webdav.read(path, callback);
        }
    },

    saveFile : function(path, data, callback) {
        if (!this.webdav)
            return;
        this.webdav.write(path, data, null, function(data, state, extra) {
            if ((state == apf.ERROR && extra.status == 400 && extra.retries < 3) || state == apf.TIMEOUT)
                return extra.tpModule.retry(extra.id);

            callback(data, state, extra);
        });
    },

    list : function(path, callback) {
        if (this.webdav)
            this.webdav.list(path, callback);
    },

    exists : function(path, callback) {
        if (this.webdav)
            this.webdav.exists(path, callback);
    },

    createFolder: function(name, tree, noRename, callback) {
        if (!tree) {
            tree = apf.document.activeElement;
            if (!tree || tree.localName != "tree")
                tree = trFiles;
        }

        var node = tree.selected;
        if (!node && tree.xmlRoot)
            node = tree.xmlRoot.selectSingleNode("folder");
        if (!node)
            return callback && callback();

        if (node.getAttribute("type") != "folder" && node.tagName != "folder")
            node = node.parentNode;

        if (this.webdav) {
            var prefix = name ? name : "New Folder";
            var path = node.getAttribute("path");
            if (!path) {
                path = ide.davPrefix;
                node.setAttribute("path", path);
            }

            var _self = this,
                index = 0;

            function test(exists) {
                if (exists) {
                    name = prefix + "." + index++;
                    _self.exists(path + "/" + name, test);
                } else {
                    tree.focus();
                    _self.webdav.exec("mkdir", [path, name], function(data) {
                        // @todo: in case of error, show nice alert dialog
                        if (!data || data instanceof Error) {
                            callback && callback();
                            throw Error;
                        }
                        
                        // parse xml
                        var nodesInDirXml = apf.getXml(data);
                        // we expect the new created file in the directory listing
                        var fullFolderPath = path + "/" + name;
                        var folder = nodesInDirXml.selectSingleNode("//folder[@path='" + fullFolderPath + "']");
                        
                        // not found? display an error
                        if (!folder) {
                            util.alert("Error", "Folder '" + name + "' could not be created",
                                 "An error occurred while creating a new folder, please try again.");
                            callback && callback();
                            return;
                        }
                        tree.slideOpen(null, node, true, function(data, flag, extra){
                            // empty data means it didn't trigger <insert> binding,
                            // therefore the node was expanded already
                            if (!data)
                                tree.add(folder, node);

                            folder = apf.queryNode(node, "folder[@path='"+ fullFolderPath +"']");

                            tree.select(folder);
                            
                            if (!noRename)
                                tree.startRename();
                            
                            callback && callback(folder);
                        });
                    });
                }
            }

            name = prefix;
            this.exists(path + "/" + name, test);
        }
    },

    createFile: function(filename, newFile) {
        var node;

        if (!newFile) {
            node = trFiles.selected;
            if (!node)
                node = trFiles.xmlRoot.selectSingleNode("folder");
            if (node.getAttribute("type") != "folder" && node.tagName != "folder")
                node = node.parentNode;
        }
        else {
            node = apf.getXml('<file newfile="1" type="file" size="" changed="1" '
                + 'name="Untitled.txt" contenttype="text/plain; charset=utf-8" '
                + 'modifieddate="" creationdate="" lockable="false" hidden="false" '
                + 'executable="false"></file>');
        }

        if (this.webdav) {
            var prefix = filename ? filename : "Untitled";

            if(!newFile)
                trFiles.focus();

            var _self = this,
                path  = node.getAttribute("path");
            if (!path) {
                path = ide.davPrefix;
                node.setAttribute("path", path);
            }

            var index = 0;

            var test = function(exists) {
                if (exists) {
                    filename = prefix + "." + index++;
                    _self.exists(path + "/" + filename, test);
                }
                else {
                    if (!newFile) {
                        var file
                        var both = 0;
                        function done(){
                            if (both == 2) {
                                file = apf.xmldb.appendChild(node, file);
                                trFiles.select(file);
                                trFiles.startRename();
                                trFiles.slideOpen(null, node, true);
                            }
                        }

                        trFiles.slideOpen(null, node, true, function(){
                            both++;
                            done();
                        });

                        _self.webdav.exec("create", [path, filename], function(data) {
                            _self.webdav.exec("readdir", [path], function(data) {
                                if (!data || data instanceof Error) {
                                    // @todo: should we display the error message in the Error object too?
                                    return util.alert("Error", "File '" + filename + "' could not be created",
                                        "An error occurred while creating a new file, please try again.");
                                }
                                
                                // parse xml
                                var filesInDirXml = apf.getXml(data);
                                
                                // we expect the new created file in the directory listing
                                var fullFilePath = path + "/" + filename;
                                var nodes = filesInDirXml.selectNodes("//file[@path='" + fullFilePath + "']");
                                
                                // not found? display an error
                                if (nodes.length === 0) {
                                    return util.alert("Error", "File '" + filename + "' could not be created",
                                        "An error occurred while creating a new file, please try again.");
                                }
                                
                                file = nodes[0];

                                both++;
                                done();
                            });
                        });
                    }
                    else {
                        node.setAttribute("name", filename);
                        node.setAttribute("path", path + "/" + filename);
                        ide.dispatchEvent("openfile", {doc: ide.createDocument(node), type:"newfile"});
                    }
                }
            };

            filename = prefix;
            this.exists(path + "/" + filename, test);
        }
    },

    beforeStopRename : function(name) {
        // Returning false from this function will cancel the rename. We do this
        // when the name to which the file is to be renamed contains invalid
        // characters
        var match = name.match(/^(?:\w|[.])(?:\w|[.-])*$/);

        return match !== null && match[0] == name;
    },

    beforeRename : function(node, name, newPath, isCopyAction, isReplaceAction) {
        var path = node.getAttribute("path");
        var page = tabEditors.getPage(path);

        if (name)
            newPath = path.replace(/^(.*\/)[^\/]+$/, "$1" + name);
        else
            name = newPath.match(/[^\/]+$/);

        node.setAttribute("oldpath", node.getAttribute("path"));
        node.setAttribute("path", newPath);
        if (isCopyAction || node.getAttribute('name') != name)
            apf.xmldb.setAttribute(node, "name", name);

        // when this is a copy action, then we don't want this to happen
        if (page && !isCopyAction)
            page.setAttribute("id", newPath);

        var childNodes = node.childNodes;
        var length = childNodes.length;

        for (var i = 0; i < length; ++i) {
            var childNode = childNodes[i];
            if(!childNode || childNode.nodeType != 1)
                continue;

            // The 'name' variable is redeclared here for some f****d up reason.
            // The problem is that we are reusing that variable below. If the author
            // of this would be so kind to fix this code as soon as he sees this
            // comment, I would be eternally grateful. Sergi.
            var name = childNode.getAttribute("name");

            this.beforeRename(childNode, null, node.getAttribute("path") + "/" + name);
        }
        
        ide.dispatchEvent("updatefile", {
            path: path,
            newPath: newPath,
            filename: name && name[0],
            xmlNode: node,
            replace: isReplaceAction
        });
    },

    beforeMove: function(parent, node, tree) {
        var path = node.getAttribute("path");
        var page = tabEditors.getPage(path);
        var newpath = parent.getAttribute("path") + "/" + node.getAttribute("name");

        node.setAttribute("path", newpath);
        if (page)
            page.setAttribute("id", newpath);

        var childNodes = node.childNodes;
        var length = childNodes.length;

        for (var i = 0; i < length; ++i) {
            this.beforeMove(node, childNodes[i]);
        }

        ide.dispatchEvent("updatefile", {
            path: path,
            xmlNode: node
        });

        return true;
    },

    remove: function(path, callback) {
        var page = tabEditors.getPage(path);
        if (page)
            tabEditors.remove(page);
        
        if(!callback)
            callback = function() {};
            
        davProject.remove(path, false, callback);
    },

    /**** Init ****/

    init : function() {
        commands.addCommand({
            name : "open",
            hint: "open a file to edit in a new tab",
            commands: {
                "[PATH]": {"hint": "path pointing to a file. Autocomplete with [TAB]"}
            }
        });
        commands.addCommand({
            name: "c9",
            hint: "alias for 'open'",
            commands: {
                "[PATH]": {"hint": "path pointing to a file. Autocomplete with [TAB]"}
            }
        });
        
        this.model = new apf.model();
        this.model.load("<data><folder type='folder' name='" + ide.projectName +
            "' path='" + ide.davPrefix + "' root='1'/></data>");

        this.model.setAttribute("whitespace", false);

        var processing = {};
        this.model.addEventListener("update", function(e){
            // Resort on move, copy, rename, add
            if (e.action === "attribute" || e.action === "add" || e.action === "move") {
                var xmlNode = e.xmlNode, pNode = xmlNode.parentNode;
                if (processing[xmlNode.getAttribute("a_id")]) {
                    return;
                }
                processing[xmlNode.getAttribute("a_id")] = true;

                var sort = new apf.Sort();
                sort.set({
                    xpath: "@name",
                    method: "filesort"
                });
                var nodes = sort.apply(pNode.childNodes);

                for (var i = 0, l = nodes.length; i < l; i++) {
                    if (nodes[i] == xmlNode) {
                        if (xmlNode.nextSibling != nodes[i+1]) {
                            apf.xmldb.appendChild(pNode, xmlNode, nodes[i+1]);
                        }
                        break;
                    }
                }
            }
        });

        var dav_url = location.href.replace(location.pathname + location.hash, "") + ide.davPrefix;
        this.webdav = new apf.webdav({
            id  : "davProject",
            url : dav_url,
            onauthfailure: function() {
                ide.dispatchEvent("authrequired");
            }
        });

        function openHandler(e) {
            ide.send({
                command: "internal-isfile",
                argv: e.data.argv,
                cwd: e.data.cwd,
                sender: "filesystem"
            });
            return false;
        }
        ide.addEventListener("consolecommand.open", openHandler);
        ide.addEventListener("consolecommand.c9",   openHandler);

        var fs = this;
        ide.addEventListener("openfile", function(e){
            var doc  = e.doc;
            var node = doc.getNode();
            var editor = e.doc.$page && e.doc.$page.$editor;

            apf.xmldb.setAttribute(node, "loading", "true");
            ide.addEventListener("afteropenfile", function(e) {
                if (e.node == node) {
                    apf.xmldb.removeAttribute(e.node, "loading");
                    ide.removeEventListener("afteropenfile", arguments.callee);
                }
            });

            if (doc.hasValue()) {
                ide.dispatchEvent("afteropenfile", {doc: doc, node: node, editor: editor});
                return;
            }

            // do we have a value in cache, then use that one
            if (doc.cachedValue) {
                doc.setValue(doc.cachedValue);
                delete doc.cachedValue;
                ide.dispatchEvent("afteropenfile", {doc: doc, node: node, editor: editor});
            }
            // if we're creating a new file then we'll fill the doc with nah dah
            else if ((e.type && e.type === "newfile") || Number(node.getAttribute("newfile") || 0) === 1) {
                doc.setValue("");
                ide.dispatchEvent("afteropenfile", {doc: doc, node: node, editor: editor});
            }
            // otherwise go on loading
            else {
                // add a way to hook into loading of files
                if (ide.dispatchEvent("readfile", {doc: doc, node: node}) === false)
                    return;

                var path = node.getAttribute("path");

                /**
                 * Callback function after we retrieve response from jsdav
                 */
                var readfileCallback = function(data, state, extra) {
                    // verify if the request succeeded
                    if (state != apf.SUCCESS) {
                        // 404's should give a file not found, but what about others?
                        // for clarity we'll console.log some info; so it'll help us debug it
                        // in case it would happen actually
                        if (extra.status !== 404) {
                            console.log("Opening file failed for", path, "server responded with", state, extra);
                        }
                        
                        // now invoke filenotfound every time
                        // because we can't be sure about the state so force a close of the file tab
                        // this will prevent things like empty files, etc.
                        ide.dispatchEvent("filenotfound", {
                            node : node,
                            url  : extra.url,
                            path : path
                        });
                    }
                    else {
                        //doc.addEventListener("editor.ready", function(){
                            // populate the document
                            doc.setValue(data);
                            
                            // fire event
                            ide.dispatchEvent("afteropenfile", { doc: doc, node: node, editor: editor });
                        //});
                    }
                };
                
                // offline / online detection has been moved into fs.readFile instead
                fs.readFile(path, readfileCallback);
            }
        });

        ide.addEventListener("reload", function(e) {
            var doc  = e.doc,
                node = doc.getNode(),
                path = node.getAttribute("path");

            /**
             * This callback is executed when the file is read, we need to check
             * the current state of online/offline
             */
            var readfileCallback = function(data, state, extra) {
                if (state == apf.OFFLINE) {
                    ide.addEventListener("afteronline", function(e) {
                        fs.readFile(path, readfileCallback);
                        ide.removeEventListener("afteronline", arguments.callee);
                    });
                } else if (state != apf.SUCCESS) {
                    if (extra.status == 404)
                        ide.dispatchEvent("filenotfound", {
                            node : node,
                            url  : extra.url,
                            path : path
                        });
                } else {
                   ide.dispatchEvent("afterreload", {doc : doc, data : data});
                }
            };

            fs.readFile(path, readfileCallback);
        });
    },

    enable : function() {},

    disable : function() {},

    destroy : function(){
        commands.removeCommandsByName(["open", "c9"]);
        
        this.webdav.destroy(true, true);
        this.model.destroy(true, true);
    }
});

});
Example #22
0
File: code.js Project: dylee/cloud9
define(function(require, exports, module) {

/*global tabEditors mnuSyntax codeEditor_dontEverUseThisVariable */

require("apf/elements/codeeditor");

var ide = require("core/ide");
var ext = require("core/ext");
var menus = require("ext/menus/menus");
var commands = require("ext/commands/commands");
var EditSession = require("ace/edit_session").EditSession;
var Document = require("ace/document").Document;
var Range = require("ace/range").Range;
var MultiSelectCommands = require("ace/multi_select").commands;
var ProxyDocument = require("ext/code/proxydocument");
var defaultCommands = require("ace/commands/default_commands").commands;
var markup = require("text!ext/code/code.xml");
var settings = require("ext/settings/settings");
var themes = require("ext/themes/themes");
var markupSettings = require("text!ext/code/settings.xml");
var editors = require("ext/editors/editors");

apf.actiontracker.actions.aceupdate = function(undoObj, undo){
    var q = undoObj.args;

    if (!undoObj.initial) {
        undoObj.initial = true;
        return;
    }

    if (undo)
        q[1].undoChanges(q[0]);
    else
        q[1].redoChanges(q[0]);
};

// name: ["Menu caption", "extensions", "content-type", "hidden|other"]
var SupportedModes = {
    abap: ["ABAP", "abap", "text/x-abap", "other"],
    asciidoc: ["AsciiDoc", "asciidoc", "text/x-asciidoc", "other"],
    c9search: ["C9Search", "c9search", "text/x-c9search", "hidden"],
    c_cpp: ["C, C++", "c|cc|cpp|cxx|h|hh|hpp", "text/x-c"],
    clojure: ["Clojure", "clj", "text/x-script.clojure"],
    coffee: ["CoffeeScript", "*Cakefile|coffee|cf", "text/x-script.coffeescript"],
    coldfusion: ["ColdFusion", "cfm", "text/x-coldfusion", "other"],
    csharp: ["C#", "cs", "text/x-csharp"],
    css: ["CSS", "css", "text/css"],
    dart: ["Dart", "dart", "text/x-dart"],
    diff: ["Diff", "diff|patch", "text/x-diff", "other"],
    glsl: ["Glsl", "glsl|frag|vert", "text/x-glsl", "other"],
    golang: ["Go", "go", "text/x-go"],
    groovy: ["Groovy", "groovy", "text/x-groovy", "other"],
    haml: ["Haml", "haml", "text/haml", "other"],
    haxe: ["haXe", "hx", "text/haxe", "other"],
    html: ["HTML", "htm|html|xhtml", "text/html"],
    jade: ["Jade", "jade", "text/x-jade"],
    java: ["Java", "java", "text/x-java-source"],
    jsp: ["JSP", "jsp", "text/x-jsp", "other"],
    javascript: ["JavaScript", "js", "application/javascript"],
    json: ["JSON", "json", "application/json"],
    jsx: ["JSX", "jsx", "text/x-jsx", "other"],
    latex: ["LaTeX", "latex|tex|ltx|bib", "application/x-latex", "other"],
    less: ["LESS", "less", "text/x-less"],
    lisp: ["Lisp", "lisp|scm|rkt", "text/x-lisp", "other"],
    liquid: ["Liquid", "liquid", "text/x-liquid", "other"],
    lua: ["Lua", "lua", "text/x-lua"],
    luapage: ["LuaPage", "lp", "text/x-luapage", "other"],
    makefile: ["Makefile", "*GNUmakefile|*makefile|*Makefile|*OCamlMakefile|make", "text/x-makefile", "other"],
    markdown: ["Markdown", "md|markdown", "text/x-markdown", "other"],
    objectivec: ["Objective-C", "m", "text/objective-c", "other"],
    ocaml: ["OCaml", "ml|mli", "text/x-script.ocaml", "other"],
    perl: ["Perl", "pl|pm", "text/x-script.perl"],
    pgsql: ["pgSQL", "pgsql", "text/x-pgsql", "other"],
    php: ["PHP", "php|phtml", "application/x-httpd-php"],
    powershell: ["Powershell", "ps1", "text/x-script.powershell", "other"],
    python: ["Python", "py", "text/x-script.python"],
    r:    ["R"    , "r", "text/x-r", "other"],
    rdoc: ["RDoc" , "Rd", "text/x-rdoc", "other"],
    rhtml:["RHTML", "Rhtml", "text/x-rhtml", "other"],
    ruby: ["Ruby", "ru|gemspec|rake|rb", "text/x-script.ruby"],
    scad: ["OpenSCAD", "scad", "text/x-scad", "other"],
    scala: ["Scala", "scala", "text/x-scala"],
    scss: ["SCSS", "scss|sass", "text/x-scss"],
    sh: ["SH", "sh|bash|bat", "application/x-sh"],
    stylus: ["Stylus", "styl|stylus", "text/x-stylus"],
    sql: ["SQL", "sql", "text/x-sql"],
    svg: ["SVG", "svg", "image/svg+xml", "other"],
    tcl: ["Tcl", "tcl", "text/x-tcl", "other"],
    text: ["Text", "txt", "text/plain", "hidden"],
    textile: ["Textile", "textile", "text/x-web-textile", "other"],
    typescript: ["Typescript", "ts|str", "text/x-typescript"],
    xml: ["XML", "xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl", "application/xml"],
    xquery: ["XQuery", "xq", "text/x-xquery"],
    yaml: ["YAML", "yaml", "text/x-yaml"]
};

var fileExtensions = {}, ModesCaption = {}, contentTypes = {}, hiddenMode = {}, otherMode = {};
Object.keys(SupportedModes).forEach(function(name) {
    var mode = SupportedModes[name];
    mode.caption = mode[0];
    mode.mime = mode[2];
    mode.hidden = mode[3] == "hidden" ? true : false;
    mode.other = mode[3] == "other" ? true : false;
    mode.ext = mode[1];
    mode.ext.split("|").forEach(function(ext) {
        fileExtensions[ext] = name;
    });
    ModesCaption[mode.caption] = name;

    hiddenMode[mode.caption] = mode.hidden;
    otherMode[mode.caption] = mode.other;

    contentTypes[mode.mime] = name;
});

module.exports = ext.register("ext/code/code", {
    name    : "Code Editor",
    extName : "ext/code/code",
    dev     : "Ajax.org",
    type    : ext.EDITOR,
    markup  : markup,
    deps    : [editors],

    nodes : [],
    menus : [],

    fileExtensions : Object.keys(fileExtensions),
    supportedModes : Object.keys(SupportedModes),
    prevSelection : null,

    getState : function(doc) {
        doc = doc ? doc.acesession : this.getDocument();
        if (!doc || typeof doc.getSelection != "function")
            return;

        var folds = doc.getAllFolds().map(function(fold) {
            return {
                start: fold.start,
                end: fold.end,
                placeholder: fold.placeholder
            };
        });

        var sel = doc.getSelection();
        return {
            scrolltop  : doc.getScrollTop(),
            scrollleft : doc.getScrollLeft(),
            selection  : sel.getRange(),
            folds      : folds
        };
    },

    setState : function(doc, state){
        var aceDoc = doc ? doc.acesession : this.getDocument();
        if (!aceDoc || !state || typeof aceDoc.getSelection != "function")
            return;

        var sel = aceDoc.getSelection();

        //are those 3 lines set the values in per document base or are global for editor
        sel.setSelectionRange(state.selection, false);

        aceDoc.setScrollTop(state.scrolltop);
        aceDoc.setScrollLeft(state.scrollleft);

        if (state.folds) {
            for (var i = 0, l=state.folds.length; i < l; i++) {
                var fold = state.folds[i];
                aceDoc.addFold(fold.placeholder, Range.fromPoints(fold.start, fold.end));
            }
        }

        // if newfile == 1 and there is text cached, restore it
        var node = doc.getNode && doc.getNode();
        if (node && parseInt(node.getAttribute("newfile") || 0, 10) === 1 && node.childNodes.length) {
            // the text is cached within a CDATA block as first childNode of the <file>
            if (doc.getNode().childNodes[0] instanceof CDATASection) {
                aceDoc.setValue(doc.getNode().childNodes[0].nodeValue);
            }
        }
    },

    getSyntax : function(node) {
        if (!node)
            return "";

        var mode = node.getAttribute("customtype");
        var ext;

        if (mode) {
            ext = contentTypes[mode.split(";")[0]] ;
            if (ext)
                mode = fileExtensions[contentTypes[mode]];
        }
        else {
            var fileName = node.getAttribute("name");
            var dotI = fileName.lastIndexOf(".") + 1;
            ext = dotI ? fileName.substr(dotI).toLowerCase() : "*" + fileName;
            mode = fileExtensions[ext];
        }

        return SupportedModes[mode] ? mode : "text";
    },

    setSyntax : function(value) {
        value = SupportedModes[value] ? value : "";
        var file = ide.getActivePageModel();
        if (!file)
            return;

        var fileName = file.getAttribute("name");
        var dotI = fileName.lastIndexOf(".") + 1;
        var ext = dotI ? fileName.substr(dotI).toLowerCase() : "*" + fileName;
        if (value) {
            if (!SupportedModes[value])
                return;

            apf.xmldb.setAttribute(file, "customtype", value);
            fileExtensions[ext] = value;
        }
        else {
            apf.xmldb.removeAttribute(file, "customtype", "");

            delete fileExtensions[ext];
            for (var mode in SupportedModes) {
                if (SupportedModes[mode].ext.split("|").indexOf(ext) != -1) {
                    fileExtensions[ext] = mode;
                    break;
                }
            }
        }

        var mime = this.setCustomType(dotI ? ext : file, value);
        ide.dispatchEvent("track_action", {
            type: "syntax highlighting",
            fileType: ext,
            fileName: fileName,
            mime: mime,
            customType: value
        });
        if (this.amlEditor)
            this.amlEditor.setAttribute("syntax", this.getSyntax(file));
    },

    getContentType : function(node) {
        var syntax = this.getSyntax(node);
        if (!syntax)
            return "auto";

        return SupportedModes[syntax].mime || "auto";
    },

    getSelection : function(){
        if (typeof this.amlEditor == "undefined")
            return null;
        return this.amlEditor.getSelection();
    },

    getDocument : function(){
        if (typeof this.amlEditor == "undefined")
            return null;
        return this.amlEditor.getSession();
    },

    setDocument : function(doc, actiontracker, isLazy){
        var _self = this;

        var amlEditor = this.amlEditor;

        if (doc.acesession) {
            amlEditor.setProperty("value", doc.acesession);
        }
        else {
            doc.isInited = doc.hasValue();
            doc.acedoc = doc.acedoc || new ProxyDocument(new Document(doc.getValue() || ""));
            var syntax = _self.getSyntax(doc.getNode());
            var mode = amlEditor.getMode(syntax);
            doc.acesession = new EditSession(doc.acedoc, mode);
            doc.acesession.syntax = syntax;
            doc.acedoc = doc.acesession.getDocument();
            doc.acesession.c9doc = doc;

            doc.acesession.setUndoManager(actiontracker);

            if (doc.isInited && doc.state)
                 _self.setState(doc, doc.state);

            var onPropValue;
            doc.addEventListener("prop.value", onPropValue = function(e) {
                if (this.editor != _self)
                    return;

                if (!doc || !doc.acesession)
                    return; //This is probably a deconstructed document

                doc.acesession.setValue(e.value || "");

                if (doc.state)
                    _self.setState(doc, doc.state);

                doc.isInited = true;

                if (this.$page.id != this.$page.parentNode.activepage)
                    return;

                amlEditor.setAttribute("syntax", syntax);
                amlEditor.setAttribute("value", doc.acesession);
                // force tokenize first visible rows
                var rowCount = Math.min(50, doc.acesession.getLength());
                doc.acesession.bgTokenizer.getTokens(0, rowCount);
            });

            if (!isLazy)
                amlEditor.setProperty("value", doc.acesession || "");

            var onRetrieveValue;
            doc.addEventListener("retrievevalue", onRetrieveValue = function(e) {
                if (this.editor != _self || !doc)
                    return;

                if (!doc.isInited)
                    return e.value;
                else
                    return doc.acesession.getValue();
            });

            var onClose;
            doc.addEventListener("close", onClose = function(e){
                if (this.editor != _self)
                    return;

                doc.removeEventListener("close", onClose);
                doc.removeEventListener("retrievevalue", onRetrieveValue);
                doc.removeEventListener("prop.value", onPropValue);

                //??? destroy doc.acesession
                setTimeout(function() {
                    if (doc.acedoc) {
                        doc.acedoc.doc.$lines = [];
                        doc.acedoc.doc._eventRegistry = null;
                        doc.acedoc.doc._defaultHandlers = null;
                        doc.acedoc._eventRegistry = null;
                        doc.acedoc._defaultHandlers = null;
                        doc.acedoc = null;
                    }
                    doc.acesession.$stopWorker();
                    doc.acesession.bgTokenizer.lines = [];
                    doc.acesession.bgTokenizer.tokenizer = null;
                    doc.acesession.bgTokenizer = null;
                    doc.acesession.$rowCache = null;
                    doc.acesession.$mode = null;
                    doc.acesession.$origMode = null;
                    doc.acesession.$breakpoints = null;
                    doc.acesession.$annotations = null;
                    doc.acesession.languageAnnos = null;
                    doc.acesession = null;
                    doc = null;
                    //??? call doc.$page.destroy()
                });
            });

            doc.dispatchEvent("init");
        }

        if (doc.editor && doc.editor != this) {
            var value = doc.getValue();
            if (doc.acesession.getValue() !== value) {
                doc.editor = this;
                doc.dispatchEvent("prop.value", {value : value});
            }
        }

        doc.editor = this;
    },

    clear : function(){
        this.amlEditor.clear();
    },

    focus : function(){
        this.amlEditor.focus();
    },

    hook: function() {
        var _self = this;
        this.wrapAceCommands();

        commands.addCommand({
            name: "syntax",
            exec: function(_, syntax) {
                if (typeof syntax == "object")
                    syntax = syntax.argv && syntax.argv[1] || "";
                syntax = ModesCaption[syntax] || fileExtensions[syntax] || syntax;
                _self.setSyntax(syntax);
            },
            commands: ModesCaption
        });

        //Settings Support
        ide.addEventListener("settings.load", function(e) {
            settings.setDefaults("editors/code", [
                ["overwrite", "false"],
                ["selectstyle", "line"],
                ["activeline", "true"],
                ["gutterline", "true"],
                ["showinvisibles", "false"],
                ["showprintmargin", "true"],
                ["showindentguides", "true"],
                ["printmargincolumn", "80"],
                ["behaviors", "true"],
                ["wrapbehaviors", "false"],
                ["softtabs", "true"],
                ["tabsize", "2"],
                ["scrollspeed", "2"],
                ["fontsize", "12"],
                ["wrapmode", "false"],
                ["wraplimitmin", ""],
                ["wraplimitmax", ""],
                ["wrapmodeViewport", "true"],
                ["gutter", "true"],
                ["folding", "true"],
                ["newlinemode", "auto"],
                ["highlightselectedword", "true"],
                ["autohidehorscrollbar", "true"],
                ["fadefoldwidgets", "true"],
                ["animatedscroll", "true"]
            ]);

            // Enable bracket insertion by default, even if it was disabled before,
            // migrating old users that had it disabled by default
            var defaulted = e.model.queryValue("editors/code/@behaviorsdefaulted");
            if (defaulted !== "true") {
                e.model.setQueryValue("editors/code/@behaviorsdefaulted", "true");
                e.model.setQueryValue("editors/code/@behaviors", "true");
                e.model.setQueryValue("editors/code/@wrapbehaviors", "false");
            }

            // pre load custom mime types
            _self.getCustomTypes(e.model);
        });

        settings.addSettings("Code Editor", markupSettings);

        ide.addEventListener("tab.afterswitch", function(e) {
            var editor = _self.amlEditor;
            if (typeof editor !== "undefined") {
                // path without dav prefix and without trailing slashes
                var path = (e.nextPage.name.indexOf(e.currentTarget.davPrefix) === 0 ?
                    e.nextPage.name.substr(e.currentTarget.davPrefix.length) :
                    e.nextPage.name).replace(/^\/+/, "");

                editor.afterOpenFile(editor.getSession(), path);
            }
        });

        this.registerMenuItems();
    },

    wrapAceCommands: function() {
        var fnWrap = function(command){
            command.readOnly = command.readOnly || false;
            command.focusContext = true;

            var isAvailable = command.isAvailable;
            command.isAvailable = function(editor, event) {
                if (event instanceof KeyboardEvent &&
                 (!apf.activeElement || apf.activeElement.localName != "codeeditor"))
                    return false;

                return isAvailable ? isAvailable(editor) : true;
            };

            command.findEditor = function(editor) {
                if (editor && editor.amlEditor)
                    return editor.amlEditor.$editor;
                return editor;
            };
        };

        if (!defaultCommands.wrapped) {
            defaultCommands.each(fnWrap, defaultCommands);
            defaultCommands.wrapped = true;
        }
        if (!MultiSelectCommands.wrapped) {
            MultiSelectCommands.each(fnWrap, MultiSelectCommands);
            MultiSelectCommands.wrapped = true;
        }

        commands.addCommands(defaultCommands, true);
        commands.addCommands(MultiSelectCommands, true);


        // Override ACE key bindings (conflict with goto definition)
        commands.commands.togglerecording.bindKey = { mac: "Command-Shift-R", win: "Alt-Shift-R" };
        commands.commands.replaymacro.bindKey = { mac: "Command-Ctrl-R", win: "Alt-R" };
        commands.addCommand(commands.commands.togglerecording);
        commands.addCommand(commands.commands.replaymacro);
    },

    registerMenuItems: function() {
        var _self = this;
        var c = 20000;
        this.menus.push(
            menus.addItemByPath("Tools/~", new apf.divider(), c += 100),
            addEditorMenu("Tools/Toggle Macro Recording", "togglerecording"), //@todo this needs some more work
            addEditorMenu("Tools/Play Macro", "replaymacro")//@todo this needs some more work
        );

        c = 600;
        this.menus.push(
            menus.addItemByPath("Edit/~", new apf.divider(), c += 100),
            menus.addItemByPath("Edit/Line/", null, c += 100),
            menus.addItemByPath("Edit/Comment/", null, c += 100),
            menus.addItemByPath("Edit/Text/", null, c += 100),
            menus.addItemByPath("Edit/Code Folding/", null, c += 100),
            menus.addItemByPath("Edit/Convert Case/", null, c += 100)
        );

        function addEditorMenu(path, commandName) {
            return menus.addItemByPath(path, new apf.item({
                command : commandName
            }), c += 100);
        }

        c = 0;
        this.menus.push(
            addEditorMenu("Edit/Line/Indent", "indent"),
            addEditorMenu("Edit/Line/Outdent", "outdent"),
            addEditorMenu("Edit/Line/Move Line Up", "movelinesup"),
            addEditorMenu("Edit/Line/Move Line Down", "movelinesdown"),

            menus.addItemByPath("Edit/Line/~", new apf.divider(), c += 100),
            addEditorMenu("Edit/Line/Copy Lines Up", "copylinesup"),
            addEditorMenu("Edit/Line/Copy Lines Down", "copylinesdown"),

            menus.addItemByPath("Edit/Line/~", new apf.divider(), c += 100),
            addEditorMenu("Edit/Line/Remove Line", "removeline"),
            addEditorMenu("Edit/Line/Remove to Line End", "removetolineend"),
            addEditorMenu("Edit/Line/Remove to Line Start", "removetolinestart"),

            menus.addItemByPath("Edit/Line/~", new apf.divider(), c += 100),
            addEditorMenu("Edit/Line/Split Line", "splitline")
        );

        c = 0;
        this.menus.push(
            addEditorMenu("Edit/Comment/Toggle Comment", "togglecomment")
        );

        c = 0;
        this.menus.push(
            addEditorMenu("Edit/Text/Remove Word Right", "removewordright"),
            addEditorMenu("Edit/Text/Remove Word Left", "removewordleft"),

            menus.addItemByPath("Edit/Text/~", new apf.divider(), c += 100),
            addEditorMenu("Edit/Text/Transpose Letters", "transposeletters")
        );

        c = 0;
        this.menus.push(
            addEditorMenu("Edit/Code Folding/Fold", "fold"),
            addEditorMenu("Edit/Code Folding/Unfold", "unfold"),

            menus.addItemByPath("Edit/Code Folding/~", new apf.divider(), c += 100),
            addEditorMenu("Edit/Code Folding/Fold All", "foldall"),
            addEditorMenu("Edit/Code Folding/Unfold All", "unfoldall")
        );

        c = 0;
        this.menus.push(
            addEditorMenu("Edit/Convert Case/Upper Case", "touppercase"),
            addEditorMenu("Edit/Convert Case/Lower Case", "tolowercase")
        );

        c = 0;
        this.menus.push(
            addEditorMenu("Selection/Select All", "selectall"),
            addEditorMenu("Selection/Split Into Lines", "splitIntoLines"),
            addEditorMenu("Selection/Single Selection", "singleSelection"),

            menus.addItemByPath("Selection/~", new apf.divider(), c += 100),
            menus.addItemByPath("Selection/Multiple Selections/", null, c += 100),

            menus.addItemByPath("Selection/~", new apf.divider(), c += 100),
            addEditorMenu("Selection/Select Word Right", "selectwordright"),
            addEditorMenu("Selection/Select Word Left", "selectwordleft"),

            menus.addItemByPath("Selection/~", new apf.divider(), c += 100),
            addEditorMenu("Selection/Select to Line End", "selecttolineend"),
            addEditorMenu("Selection/Select to Line Start", "selecttolinestart"),

            menus.addItemByPath("Selection/~", new apf.divider(), c += 100),
            addEditorMenu("Selection/Select to Document End", "selecttoend"),
            addEditorMenu("Selection/Select to Document Start", "selecttostart")
        );

        c = 0;
        this.menus.push(
            addEditorMenu("Selection/Multiple Selections/Add Cursor Up", "addCursorAbove"),
            addEditorMenu("Selection/Multiple Selections/Add Cursor Down", "addCursorBelow"),
            addEditorMenu("Selection/Multiple Selections/Move Active Cursor Up", "addCursorAboveSkipCurrent"),
            addEditorMenu("Selection/Multiple Selections/Move Active Cursor Down", "addCursorBelowSkipCurrent"),

            menus.addItemByPath("Selection/Multiple Selections/~", new apf.divider(), c += 100),
            addEditorMenu("Selection/Multiple Selections/Add Next Selection Match", "selectMoreAfter"),
            addEditorMenu("Selection/Multiple Selections/Add Previous Selection Match", "selectMoreBefore"),

            menus.addItemByPath("Selection/Multiple Selections/~", new apf.divider(), c += 100),
            addEditorMenu("Selection/Multiple Selections/Merge Selection Range", "splitIntoLines")
        );

        var grpSyntax, grpNewline;

        /**** View ****/
        this.menus.push(
            menus.addItemByPath("View/Gutter", new apf.item({
                type    : "check",
                checked : "[{require('core/settings').model}::editors/code/@gutter]"
            }), 500),

            menus.addItemByPath("View/~", new apf.divider(), 290000),

            menus.addItemByPath("View/Syntax/", new apf.menu({
                "onprop.visible" : function(e){
                    if (e.value) {
                        if (!editors.currentEditor || !editors.currentEditor.amlEditor)
                            this.disable();
                        else {
                            this.enable();

                            var page = tabEditors.getPage();
                            var node = page && page.$model.data;
                            grpSyntax.setValue(node
                                && node.getAttribute("customtype") || "auto");
                        }
                    }
                },
                "onitemclick" : function(e) {
                    _self.setSyntax(e.relatedNode.value);
                }
            }), 300000),

            grpNewline = new apf.group(),

            menus.addItemByPath("View/Newline Mode/", new apf.menu({
                "onprop.visible" : function(e){
                    if (e.value) {
                        grpNewline.setValue(
                            settings.model.queryValue("editors/code/@newlinemode"));
                    }
                },
                "onitemclick" : function(e){
                    settings.model.setQueryValue("editors/code/@newlinemode",
                        e.relatedNode.value);
                }
            }), 310000),

            menus.addItemByPath("View/Newline Mode/Auto", new apf.item({
                type    : "radio",
                value   : "auto",
                group   : grpNewline
            }), 100),

            menus.addItemByPath("View/Newline Mode/~", new apf.divider(), 110),

            menus.addItemByPath("View/Newline Mode/Windows (CRLF)", new apf.item({
                type    : "radio",
                value   : "windows",
                group   : grpNewline
            }), 200),

            menus.addItemByPath("View/Newline Mode/Unix (LF)", new apf.item({
                type    : "radio",
                value   : "unix",
                group   : grpNewline
            }), 300),

            menus.addItemByPath("View/~", new apf.divider(), 400000),

            menus.addItemByPath("View/Wrap Lines", new apf.item({
                type    : "check",
                checked : "[{tabEditors.activepage && tabEditors.getPage(tabEditors.activepage).$model}::@wrapmode]"
            }), 500000),

            menus.addItemByPath("View/Wrap To Viewport", new apf.item({
                id : "mnuWrapView",
                type     : "check",
                checked  : "[{require('core/settings').model}::editors/code/@wrapmodeViewport]"
            }), 600000)
        );

        c = 0;

        var otherGrpSyntax;
        this.menus.push(
            grpSyntax = new apf.group(),

            menus.addItemByPath("View/Syntax/Auto-Select", new apf.item({
                type: "radio",
                value: "auto",
                group : grpSyntax
            }), c += 100),

            menus.addItemByPath("View/Syntax/Plain Text", new apf.item({
                type: "radio",
                value: "text/plain",
                group : grpSyntax
            }), c += 100),

            otherGrpSyntax = new apf.group({
                type : ""
            }),

            menus.addItemByPath("View/Syntax/Other", new apf.item({
                group : otherGrpSyntax
            }), c + 90000),

            menus.addItemByPath("View/Syntax/~", new apf.divider(), c += 100)
        );

        function onModeClick(e) {
            if (!_self.prevSelection)
                _self.prevSelection = this;
            else {
                _self.prevSelection.uncheck();
                if (_self.prevSelection.group.selectedItem.caption == "Other") {
                    _self.prevSelection.group.selectedItem.$ext.setAttribute("class", "menu_item submenu");
                }
                _self.prevSelection = this;
            }
        }

        for (var mode in ModesCaption) {
            if (hiddenMode[mode])
                continue;

            this.menus.push(
                menus.addItemByPath("View/Syntax/" + (otherMode[mode] ? "Other/" + mode : mode), new apf.item({
                    type: "radio",
                    value: ModesCaption[mode],
                    group : otherMode[mode] ? otherGrpSyntax : grpSyntax,
                    onclick : onModeClick
                }), c += 100)
            );
        }

        c = 0;
        this.menus.push(
            /**** Goto ****/

            menus.addItemByPath("Goto/~", new apf.divider(), c = 399),

            addEditorMenu("Goto/Word Right", "gotowordright"),
            addEditorMenu("Goto/Word Left", "gotowordleft"),
            menus.addItemByPath("Goto/~", new apf.divider(), 600),

            addEditorMenu("Goto/Line End", "gotolineend"),
            addEditorMenu("Goto/Line Start", "gotolinestart"),
            menus.addItemByPath("Goto/~", new apf.divider(), c += 100),

            addEditorMenu("Goto/Jump to Matching Brace", "jumptomatching"),
            menus.addItemByPath("Goto/~", new apf.divider(), c += 100),

            addEditorMenu("Goto/Scroll to Selection", "centerselection")
        );

        ide.addEventListener("tab.afterswitch", function(e) {
            var method = e.nextPage.$editor.path != "ext/code/code" ? "disable" : "enable";

            menus.menus["Edit"][method]();
            menus.menus["Selection"][method]();
            menus.menus["Find"][method]();
            menus.menus["View/Syntax"][method]();
            menus.menus["View/Font Size"][method]();
            menus.menus["View/Syntax/Other"][method]();
            menus.menus["View/Syntax"][method]();
            menus.menus["View/Newline Mode"][method]();
            // WY U NO WORK??
            menus.menus["Goto"][method]();
        });
    },

    init: function(amlPage) {
        var _self = this;

        _self.amlEditor = codeEditor_dontEverUseThisVariable;
        _self.amlEditor.show();

        _self.amlEditor.$editor.$nativeCommands = _self.amlEditor.$editor.commands;
        _self.amlEditor.$editor.commands = commands;

        // preload common language modes
        var noop = function() {};
        _self.amlEditor.getMode("javascript", noop);
        _self.amlEditor.getMode("html", noop);
        _self.amlEditor.getMode("css", noop);

        ide.addEventListener("reload", function(e) {
            var doc = e.doc;
            doc.state = doc.$page.$editor.getState
                && doc.$page.$editor.getState(doc);
        });

        ide.addEventListener("afterreload", function(e) {
            var doc         = e.doc;
            var acesession  = doc.acesession;

            if (!acesession)
                return;

            acesession.doc.setValue(e.data);

            if (doc.state) {
                var editor = doc.$page.$editor;
                editor.setState && editor.setState(doc, doc.state);
            }
        });

        ide.addEventListener("updatefile", function(e){
            var page = tabEditors.getPage(e.xmlNode.getAttribute("path"));
            if (!page || !page.$doc || !page.$doc.acesession)
                return;

            // this needs to be called after rename but there is only event before
            setTimeout(function() {
                var doc = page.$doc;
                var syntax = _self.getSyntax(doc.getNode());
                // This event is triggered also when closing files, so session may be gone already.
                if(doc.acesession) {
                    doc.acesession.setMode(_self.amlEditor.getMode(syntax));
                    doc.acesession.syntax = syntax;
                }
            });
        });

        ide.addEventListener("afteroffline", function(){
            menus.menus["View/Syntax"].disable();
        });

        ide.addEventListener("afteronline", function(){
            menus.menus["View/Syntax"].enable();
        });

        ide.addEventListener("animate", function(e){
            if (!_self.amlEditor.$ext.offsetHeight)
                return;

            var renderer, delta;
            if (e.type == "editor") {
                renderer = _self.amlEditor.$editor.renderer;
                renderer.onResize(true, null, null, _self.amlEditor.getHeight() + e.delta);
            }
            else if (e.type == "splitbox") {
                if (e.options.height !== undefined && apf.isChildOf(e.other, _self.amlEditor, true)) {
                    delta = e.which.getHeight() - Number(e.options.height);
                    if (delta < 0) return;

                    renderer = _self.amlEditor.$editor.renderer;
                    renderer.onResize(true, null, null, _self.amlEditor.getHeight() + delta);
                }
                else if (e.options.width !== undefined && apf.isChildOf(e.other, _self.amlEditor, true)) {
                    delta = e.which.getWidth() - Number(e.options.width);
                    if (delta < 0) return;

                    renderer = _self.amlEditor.$editor.renderer;
                    renderer.onResize(true, null, _self.amlEditor.getWidth() + delta);
                }
            }
        });

        // display feedback while loading files
        var isOpen, bgMessage, loaderProgress, loaderBg, updateProgressInterval, animationEndTimeout;
        var checkLoading = function(e) {
            if (!_self.amlEditor.xmlRoot)
                return;
            var xmlRoot = _self.amlEditor.xmlRoot;
            var loading = xmlRoot.hasAttribute("loading");
            var container = _self.amlEditor.$editor.container;

            // loading calcs
            var padding = container.offsetWidth / 5;
            var loadingWidth = container.offsetWidth - 2*padding;

            function updateProgress (progress) {
                xmlRoot.setAttribute("loading_progress", progress);
                loaderProgress.style.width = (progress * loadingWidth / 100) + "px";
            }

            function clearProgress() {
                xmlRoot.setAttribute("loading_progress", 0);
                loaderProgress.style.width = 0 + "px";
            }

            if (loading) {
                var theme = themes.getActiveTheme();
                if (!bgMessage) {
                    bgMessage = document.createElement("div");
                    bgMessage.className = "ace_smooth_loading";

                    loaderBg = document.createElement("div");
                    loaderBg.className = "loading_bg";

                    bgMessage.appendChild(loaderBg);

                    loaderProgress = document.createElement("div");
                    loaderProgress.className = "loading_progress";
                    bgMessage.appendChild(loaderProgress);
                    updateProgress(10);
                }

                if (!bgMessage.parentNode)
                    container.parentNode.appendChild(bgMessage);

                bgMessage.style.backgroundColor = theme ? theme.bg : "gray";
                loaderBg.style.width = loadingWidth + "px";
                loaderBg.style.left = padding + "px";
                loaderBg.style.top = 0.4 * container.offsetHeight + "px";
                loaderProgress.style.left = padding + "px";
                loaderProgress.style.top = 0.4 * container.offsetHeight + "px";
                container.style.transitionProperty = "opacity";
                container.style.transitionDuration = "100ms";
                container.style.pointerEvents = "none";
                container.style.opacity = 0;
                isOpen = true;

                clearInterval(updateProgressInterval);
                updateProgressInterval = setInterval(function () {
                    var progress = parseFloat(xmlRoot.getAttribute("loading_progress"), 10);
                    var step;
                    if (progress < 35)
                        step = 3;
                    else if (progress < 70)
                        step = 1;
                    else if (progress < 90)
                        step = 0.25;
                    if (step)
                        updateProgress(progress + step);
                }, 30);
                clearTimeout(animationEndTimeout);
            } else if (isOpen) {
                clearInterval(updateProgressInterval);
                updateProgress(100);
                setTimeout(function () {
                    isOpen = false;
                    clearProgress();
                    container.style.opacity = 1;
                    container.style.pointerEvents = "";
                }, 40);

                animationEndTimeout = setTimeout(function() {
                    if (bgMessage.parentNode)
                        bgMessage.parentNode.removeChild(bgMessage);
                }, 100);
            }
        };

        ide.addEventListener("openfile", function(){
            setTimeout(checkLoading, 0);
        });
        ide.addEventListener("afteropenfile", checkLoading);
        ide.addEventListener("tab.afterswitch", checkLoading);
    },

    /**
     * Saves custom syntax for extension type in settings.xml
     *
     * @param {String|xmlNode} ext Contains the extension type shorthand
     * @param {String} mode ace mode the extension will be related to
     */
    setCustomType: function(ext, mode) {
        var node;

        if (typeof ext === "string") {
            node = settings.model.queryNode('auto/customtypes/mime[@ext="' + ext + '"]');
            if (!node && mode)
                node = settings.model.appendXml('<mime ext="' + ext + '" />', "auto/customtypes");
        } else {
            var name = ext.getAttribute("name") || "";
            node = settings.model.queryNode('auto/customtypes/mime[@filename="' + name + '"]');
            if (node)
                apf.xmldb.removeAttribute(node, "ext");
            else if (mode)
                node = settings.model.appendXml('<mime filename="' + name + '" />', "auto/customtypes");
        }
        if (mode)
            apf.xmldb.setAttribute(node, "name", mode);
        else if (node)
            apf.xmldb.removeNode(node);
        settings.save();
        return mode && SupportedModes[mode].mime;
    },

    /**
     * Retrieves custom syntax for extensions saved in settings.xml
     *
     * @param {Object} model Settings' model
     */
    getCustomTypes: function(model) {
        var customTypes = model.queryNode("auto/customtypes");
        if (!customTypes)
            customTypes = apf.createNodeFromXpath(model.data, "auto/customtypes");

        var mimes = customTypes.selectNodes("mime");
        mimes.forEach(function(n) {
            var ext = n.getAttribute("filename");
            ext = ext ? "*" + ext : n.getAttribute("ext");
            var mode = n.getAttribute("name");
            // old settings contained contenttype instead of mode
            if (contentTypes[mode])
                mode = contentTypes[mode];
            if (SupportedModes[mode])
                fileExtensions[ext] = mode;
        });
    },

    enable : function() {
        this.$enable();

        this.nodes.each(function(item){
            item.show();
        });
    },

    disable : function() {
        this.$disable();

        this.nodes.each(function(item){
            item.hide();
        });
    },

    destroy : function(){
        this.menus.each(function(item){
            item.destroy(true, true);
        });

        commands.removeCommands(defaultCommands);
        commands.removeCommands(MultiSelectCommands);

        if (this.amlEditor) {
            this.amlEditor.destroy(true, true);
            mnuSyntax.destroy(true, true);
        }
        this.$destroy();
    }
});

});
Example #23
0
define(function(require, exports, module) {

var ext = require("core/ext");
var ide = require("core/ide");
var menus = require("ext/menus/menus");
var DockableLayout = require("ext/dockpanel/libdock");
var settings = require("ext/settings/settings");
var anims = require("ext/anims/anims");

module.exports = ext.register("ext/dockpanel/dockpanel", {
    name: "Dock Panel",
    dev: "Ajax.org",
    alone: true,
    type: ext.GENERAL,

    defaultState: {
        bars: []
    },

    nodes: [],
    dockpanels: {},

    loaded: false,

    initDocTitle: document.title,
    notificationMsgs: {},

    /**
     * Standard Extension functionality
     */
    init : function(amlNode){
        var _self = this;

        var vManager = new apf.visibilitymanager();
        this.layout = new DockableLayout(hboxDockPanel,
            //Find Page
            function(arrExtension){
                if (!arrExtension || !_self.dockpanels[arrExtension[0]])
                    return false;

                var item = _self.dockpanels[arrExtension[0]][arrExtension[1]];
                if (!item)
                    return false;

                if (item.page)
                    return item.page;

                var page = item.getPage();

                if (page)
                    page.$arrExtension = arrExtension;

                vManager.permanent(page, function(e){
                    item.mnuItem.check();
                }, function(){
                    item.mnuItem.uncheck();
                });

                return page;
            },
            //Store Page
            function(amlPage){
                var arrExtension = amlPage.$arrExtension;
                var item = _self.dockpanels[arrExtension[0]][arrExtension[1]];

                item.page = amlPage;
                item.mnuItem.uncheck();

                _self.saveSettings();
            },
            //@todo This can be deprecated
            //Find Button Options
            function(arrExtension){
                if (!arrExtension || !_self.dockpanels[arrExtension[0]])
                    return false;

                var item = _self.dockpanels[arrExtension[0]][arrExtension[1]];

                return item && item.options;
            },
            //Change State Handler
            function(){
                _self.saveSettings();
            },
            //Animate Settings
            function(){
                return apf.isTrue(settings.model.queryValue('general/@animateui'));
            }
        );

        ide.addEventListener("extload", function(e){
            ide.addEventListener("settings.load", function(e){
                var model = settings.model;
                var strSettings = model.queryValue("auto/dockpanel/text()");

                var state = _self.defaultState;
                if (strSettings) {
                    // JSON parse COULD fail
                    try {
                        state = JSON.parse(strSettings);
                    }
                    catch (ex) {}
                }

                ide.dispatchEvent("dockpanel.load.settings", {state: state});
                _self.layout.loadState(state);
                _self.loaded = true;

                _self.setParentHboxTop(
                    apf.isFalse(settings.model.queryValue("auto/tabs/@show")) ? -15 : 0,
                    apf.isFalse(settings.model.queryValue("general/@animateui"))
                );
            });
        });

        ide.addEventListener("tabs.visible", function(e){
            _self.setParentHboxTop(!e.value ? -15 : 0, e.noanim);
        });

        this.nodes.push(
            menus.addItemByPath("View/Dock Panels/", null, 150),

            menus.addItemByPath("View/Dock Panels/Restore Default", new apf.item({
                onclick : function(){
                    var defaultSettings = _self.defaultState,//settings.model.queryValue("auto/dockpanel_default/text()"),
                        state;

                    if (defaultSettings) {
                        // JSON parse COULD fail
                        try {
                            state = defaultSettings;//objSettings.state;
                        }
                        catch (ex) {}
                        _self.layout.loadState(state);

                        settings.model.setQueryValue("auto/dockpanel/text()", state);

                        _self.saveSettings();

                        ide.dispatchEvent("restorelayout");
                    }
                }
            }), 100),

            menus.addItemByPath("View/Dock Panels/~", new apf.divider(), 200)
        );
    },

    setParentHboxTop : function(top, noAnim){
        if (noAnim) {
            hboxDockPanel.$ext.style.top = top + "px";
        }
        else {
            anims.animate(hboxDockPanel.$ext, {
                top: top + "px"
            });
        }
    },

    saveSettings : function(){
        clearTimeout(this.$timer);

        var _self = this;
        this.$timer = setTimeout(function(){
            var state = _self.layout.getState();

            settings.model.setQueryValue(
                "auto/dockpanel/text()",
                JSON.stringify(state)
            );
        });
    },

    enable : function(){
        if (this.$lastState)
            this.layout.loadState(this.$lastState);
    },

    disable : function(){
        this.$lastState = this.layout.getState();
        this.layout.clearState();
    },

    destroy : function(){
        menus.remove("View/Dock Panels/Restore Default");
        menus.remove("View/Dock Panels/~", 200);
        this.layout.clearState();
        this.$destroy();
    },

    register : function(name, type, options, getPage){
        var panel = this.dockpanels[name] || (this.dockpanels[name] = {});
        panel[type] = {
            options : options,
            getPage : getPage
        };
        var layout = this.layout, _self = this;

        panel[type].mnuItem = menus.addItemByPath(
          "View/Dock Panels/" + options.menu.split("/").pop(),
          new apf.item({
            id      : "mnu" + type,
            type    : "check",
            onclick : function(){
                var page = getPage();

                var uId = _self.getButtons(name, type)[0].uniqueId;
                layout.show(uId, true);
                if (layout.isExpanded(uId) < 0)
                    layout.showMenu(uId);

                page.parentNode.set(page);
            }
        }));
    },

    //@todo
    unregister : function() {

    },

    addDockable : function(def){
        var state = this.defaultState;

        if (!def.barNum)
            def.barNum = 0;

        if (def.sections) {
            if (def.barNum || def.barNum === 0) {
                if (state.bars[def.barNum])
                    state.bars[def.barNum].sections.merge(def.sections);
                else
                    state.bars[def.barNum] = def;
            }
            else
                state.bars.push(def);

            return;
        }

        if (!state.bars[def.barNum || 0])
            state.bars[def.barNum || 0] = {expanded: false, width: 230, sections: []};

        var bar = state.bars[def.barNum || 0];

        if (def.buttons) {
            bar.sections.push(def);
        }
        else {
            bar.sections.push({
                flex    : 2,
                width   : 260,
                height  : 350,
                buttons : [def]
            });
        }

        return bar.sections.slice(-1);
    }, //properties.forceShow ??

    removeButton : function(name, type, state, removeItem){
        var _self = this;
        state = state || this.layout.getState(true);
        if(!state || !name || !type)
            return;

        if(!state.bars)
            state = state.state;

        state.bars.each(function(bar){
            bar.sections.each(function(section){
                for (var k = 0; k < section.buttons.length; k++) {
                    var button = section.buttons[k];
                    if (button.ext[0] == name && button.ext[1] == type) {
                        if(removeItem)
                            delete menus.items["View/Dock Panels/" + button.caption];
                        section.buttons.remove(button);
                        delete _self.dockpanels[name][type];
                    }
                }
            });
        });
    },

    getButtons : function(name, type, state){
        state = state || this.layout.getState(true);
        var list  = [];

        if(!state)
            return list;

        if(!state.bars)
            state = state.state;

        state.bars.each(function(bar){
            bar.sections.each(function(section){
                section.buttons.each(function(button){
                    if ((!name || button.ext[0] == name)
                      && (!type || button.ext[1] == type))
                        list.push(button);
                });
            });
        });

        return list;
    },

    getBars : function(name, type, state){
        state = state || this.layout.getState(true);
        var list  = [];

        if(!state)
            return;

        if(!state.bars)
            state = state.state;

        if (!state.bars)
            return list;

        state.bars.each(function(bar){
            var found = false;
            bar.sections.each(function(section){
                section.buttons.each(function(button){
                    if ((!name || button.ext[0] == name)
                      && (!type || button.ext[1] == type))
                        found = true;
                });
            });

            if (found)
                list.push(bar);
        });

        return list;
    },

    hideSection: function(name, collapse){
        var buttons = this.getButtons(name);
        var bars = [];
        var _self = this;

        buttons.each(function(button){
            if (button.hidden < 0)
                bars.pushUnique(_self.layout.findBar(button.uniqueId));
            if (button.hidden == -1)
                _self.layout.hide(button.uniqueId);
        });

        if (collapse) {
            bars.each(function(bar){
                if (bar.expanded == 1)
                    _self.layout.collapseBar(bar.uniqueId);
            });
        }
    },

    showSection: function(name, expand){
        var buttons = this.getButtons(name);
        var _self = this;
        var bars = [];

        buttons.each(function(button){
            if (button.hidden && button.hidden == 1) {
                _self.layout.show(button.uniqueId);
                bars.pushUnique(_self.layout.findBar(button.uniqueId));
            }
        });

        bars.each(function(bar){
            if (expand && bar.expanded < 0)
                _self.layout.expandBar(bar.uniqueId);
        });
    },

    showBar : function(bar){
        if (bar.cache) {
            bar.cache.show();
            return;
        }

        var _self = this;
        bar.sections.each(function(section){
            section.buttons.each(function(button){
                _self.layout.show(button.uniqueId);
            });
        });
    },

    hideBar : function(bar){
        if (bar.cache)
            bar.cache.hide();
    },

    expandBar : function(bar){
        this.layout.expandBar(bar.cache);
    },

    //@todo removal of pages

    /**
     * Increases the notification number count by one
     *
     * @windowIdent identifier of the dock object
     */
    increaseNotificationCount: function(windowIdent){
        /*for(var doi = 0; doi < this.dockObjects.length; doi++) {
            if (this.dockObjects[doi].ident == windowIdent) {
                // Only increase notification count if window is hidden
                if (this.dockObjects[doi].btn.value == false) {
                    if (this.dockObjects[doi].notCount >= 99)
                        return true;

                    this.dockObjects[doi].notCount++;
                    this.updateNotificationElement(
                            this.dockObjects[doi].btn
                            , this.dockObjects[doi].notCount
                    );
                }

                return true;
            }
        }

        return false;*/
    },

    /**
     * Resets the notification count to 0
     */
    resetNotificationCount: function(windowIdent){
        if (windowIdent === -1) return;
        if (!this.dockObjects) return;

        for(var doi = 0; doi < this.dockObjects.length; doi++) {
            if (this.dockObjects[doi].ident == windowIdent) {
                this.dockObjects[doi].notCount = 0;
                this.updateNotificationElement(this.dockObjects[doi].btn, 0);
                return true;
            }
        }

        return false;
    },

    /**
     * Updates the notification element to visually reflect notCount
     */
    updateNotificationElement: function(btnObj, count, options){
        if (!btnObj) {
            return console.trace("dockpanel.updateNotificationElement requires btnObj not to be null");
        }

        var countInner = count === 0 ? "" : count;
        var notificationEl = btnObj.$ext.getElementsByClassName("dock_notification")[0];

        if(!options)
            options = {};

        var notificationType = options.type || "";

        if (apf.isGecko)
            notificationEl.textContent = countInner;
        else
            notificationEl.innerText = countInner;

        if (count > 0)
            notificationEl.style.display = "block";
        else
            notificationEl.style.display = "none";

        var btnPage = btnObj.$dockpage;
        if(!btnPage.initCaption)
            btnPage.initCaption = btnPage.getAttribute("caption");

        var caption = btnPage.initCaption;

        if(!btnPage.$active) {
            if (count > 0) {
                caption += " (" + count + ")";
                apf.setStyleClass(btnPage.$button, "un-read-message");
                if(notificationType == "chat") {
                    btnObj.notificationOpt = options;
                    if (options.name) {
                        this.notificationMsgs[options.name] = count;
                        this.updateDocTitleNotifications();
                    }
                }
            }
            else {
                apf.setStyleClass(btnPage.$button, "", ["un-read-message"]);
                if(notificationType == "chat" && btnObj.notificationOpt) {
                    if (btnObj.notificationOpt && btnObj.notificationOpt.name) {
                        delete this.notificationMsgs[btnObj.notificationOpt.name];
                        this.updateDocTitleNotifications();
                    }
                }
            }
            btnPage.setAttribute("caption", caption);
        }
        else {
            btnPage.setAttribute("caption", caption);
            apf.setStyleClass(btnPage.$button, "", ["un-read-message"]);
            if(notificationType == "chat" && btnObj.notificationOpt) {
                if (btnObj.notificationOpt && btnObj.notificationOpt.name) {
                    delete this.notificationMsgs[btnObj.notificationOpt.name];
                    this.updateDocTitleNotifications();
                }
            }
        }

        return true;
    },

    updateDocTitleNotifications: function(){
        var _self = this,
            countMsg = 0;

        for(var i in this.notificationMsgs) {
            if(this.notificationMsgs.hasOwnProperty(i))
                countMsg += this.notificationMsgs[i];
        }

        if(countMsg > 0) {
            if(this.barNotificationTimer)
                clearInterval(this.barNotificationTimer);

            this.barNotificationCount = 0;
            this.barNotificationTimer = setInterval(function(){
                if(_self.barNotificationCount%2)
                    document.title = "(" + countMsg + ") message" + (countMsg > 1 ? "s" : "") + " | " + _self.initDocTitle;
                else
                    document.title = _self.initDocTitle;

                _self.barNotificationCount++;
            }, 1000);
        }
        else {
            if(this.barNotificationTimer) {
                clearInterval(this.barNotificationTimer);
                this.barNotificationCount = 0;
                document.title = _self.initDocTitle;
            }
        }
    }
});
});
Example #24
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var settings = require("ext/settings/settings");
var menus = require("ext/menus/menus");
var dock = require("ext/dockpanel/dockpanel");
var editors = require("ext/editors/editors");
var markup = require("text!ext/preview/preview.xml");
var skin    = require("text!ext/preview/skin.xml");
var css     = require("text!ext/preview/style/style.css");
var markupSettings = require("text!ext/preview/settings.xml");

var $name = "ext/preview/preview";

module.exports = ext.register($name, {
    name    : "Preview",
    dev     : "Ajax.org",
    type    : ext.GENERAL,
    alone   : true,
    markup  : markup,
    $name   : $name,
    $button : "pgPreview",
    skin    : {
        id   : "previewskin",
        data : skin,
        "media-path" : ide.staticPrefix + "/ext/preview/style/images/",
        "icon-path"  : ide.staticPrefix + "/ext/preview/style/icons/"
    },
    css     : util.replaceStaticPrefix(css),
    deps    : [editors],
    autodisable : ext.ONLINE | ext.LOCAL,
    disableLut: {
        "terminal": true
    },
    nodes   : [],
    live    : null,

    _getDockBar: function () {
        return dock.getBars(this.$name, this.$button)[0];
    },

    _getDockButton: function () {
        return dock.getButtons(this.$name, this.$button)[0];
    },

    onLoad: function () {

    },

    hook: function() {
        return;
        var _self = this;

        settings.addSettings("General", markupSettings);

        this.nodes.push(
            menus.$insertByIndex(barTools, new apf.button({
                skin : "c9-toolbarbutton-glossy",
                //icon : "preview.png",
                "class" : "preview",
                tooltip : "Preview in browser",
                caption : "Preview",
                disabled : true,
                onclick : function() {
                    var page = ide.getActivePage();
                    if (page.$editor === _self)
                        return;
                    var doc = page.$doc;
                    var path = doc.getNode().getAttribute("path");
                    var url = location.protocol + "//" + location.host + path;
                    _self.preview(url, {path: path, value: doc.getValue()});
                }
            }), 10)
        );

        dock.addDockable({
            expanded : -1,
            width : 400,
            "min-width" : 400,
            barNum: 2,
            headerVisibility: "false",
            sections : [{
                width : 360,
                height: 300,
                buttons : [{
                    caption: "Preview Apps",
                    ext : [this.$name, this.$button],
                    hidden : false
                }]
            }]
        });

        dock.register(this.$name, this.$button, {
            menu : "Preview Apps",
            primary : {
                backgroundImage: ide.staticPrefix + "/ext/main/style/images/sidebar_preview_icon.png",
                defaultState: { x: -11, y: -10 },
                activeState:  { x: -11, y: -46 }
            }
        }, function() {
            ext.initExtension(_self);
            return pgPreview;
        });

        ide.addEventListener("tab.afterswitch", function(e){
            _self.enable();
        });

        ide.addEventListener("afterfilesave", _self.onFileSave.bind(_self));

        ide.addEventListener("closefile", function(e){
            if (tabEditors.getPages().length == 1)
                _self.disable();
        });

        ide.addEventListener("settings.save", function(e){
            if (_self.inited) {
                var url = txtPreview.getValue();
                if (url) {
                    var prev = {
                        visible: _self.isVisible(),
                        url: url,
                        live: _self.live && {path: _self.live.path}
                    };
                    e.model.setQueryValue("auto/preview/text()", JSON.stringify(prev));
                }
            }
        });

        ide.addEventListener("extload", function(e){
            ide.addEventListener("settings.load", function(e){
                settings.setDefaults("preview", [
                    ["running_app", "false"]
                ]);

                var json = e.model.queryValue("auto/preview/text()");
                if (json) {
                    var prev = JSON.parse(json);
                    if (prev.visible)
                        _self.refresh(prev.url, prev.live);
                }
            });
        });

        ide.addEventListener("dockpanel.loaded", function (e) {
            _self.hidePageHeader();
        });

        ext.initExtension(this);
    },

    isVisible: function () {
        var button = this._getDockButton();
        return button && button.hidden && button.hidden === -1;
    },

    // Patch the docked section to remove the page caption
    hidePageHeader: function () {
        var button = this._getDockButton();
        if (!button || !button.cache)
            return;
        var pNode = button.cache.$dockpage.$pHtmlNode;
        if (pNode.children.length === 4) {
            pNode.removeChild(pNode.children[2]);
            pNode.children[2].style.top = 0;
        }
    },

    onFileSave: function () {
        if (!this.live)
            return;
        var _self = this;
        var page = tabEditors.getPages().filter(function (page) {
            return _self.live.path === page.$doc.getNode().getAttribute("path");
        })[0];
        if (page)
            this.live.value = page.$doc.getValue();
        var iframe = this.getIframe().$ext;
        if (!iframe || !iframe.contentWindow)
            return;
        var html = iframe.contentWindow.document.getElementsByTagName("html")[0];
        html.innerHTML = this.live.value;
    },

    preview: function (url, live) {
        var bar = this._getDockBar();
        dock.showBar(bar);
        dock.expandBar(bar);
        dock.showSection(this.$name, this.$button);
        this.hidePageHeader();
        var frmPreview = this.getIframe();
        if (frmPreview.$ext.src !== url)
            this.refresh(url);
        this.live = live;
    },

    popup: function (url) {
        url = url || txtPreview.getValue();
        window.open(url, "_blank");
    },

    refresh: function (url) {
        var frmPreview = this.getIframe();
        url = url || txtPreview.getValue();
        frmPreview.$ext.src = url;
        txtPreview.setValue(url);
        settings.save();
    },

    close: function () {
        dock.hideSection(this.$name, this.$button);
        this.live = null;
        settings.save();
    },

    init: function() {
        apf.importCssString(this.css || "");
    },

    getIframe: function() {
        return pgPreview.selectSingleNode("iframe");
    },

    enable : function() {
        var page = ide.getActivePage();
        var contentType = (page && page.getModel().data.getAttribute("contenttype")) || "";
        if(this.disableLut[contentType])
            return this.disable();
        this.$enable();
    },

    disable: function() {
        this.live = null;
        this.$disable();
    }
});

});
Example #25
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var console = require("ext/console/console");
var editors = require("ext/editors/editors");
var panels = require("ext/panels/panels");
var fs = require("ext/filesystem/filesystem");
var noderunner = require("ext/noderunner/noderunner");
var markup = require("text!ext/debugger/debugger.xml");

return ext.register("ext/debugger/debugger", {
    name   : "Debug",
    dev    : "Ajax.org",
    type   : ext.GENERAL,
    alone  : true,
    markup : markup,
    deps   : [fs, noderunner],
    commands: {
        "debug": {
            "hint": "run and debug a node program on the server",
            "commands": {
                "[PATH]": {"hint": "path pointing to an executable. Autocomplete with [TAB]"}
            }
        }
    },

    nodes : [],

    hook : function(){
        this.$layoutItem = mnuModes.appendChild(new apf.item({
            value   : "ext/debugger/debugger",
            caption : this.name
        }));

        ide.addEventListener("consolecommand.debug", function(e) {
            ide.socket.send(JSON.stringify({
                command: "internal-isfile",
                argv: e.data.argv,
                cwd: e.data.cwd,
                sender: "debugger"
            }));
            return false;
        });
        
        var _self = this;
        stDebugProcessRunning.addEventListener("activate", function() {            
            _self.enable();
        });
        stProcessRunning.addEventListener("deactivate", function() {
            _self.disable();
        });
        
        panels.register(this);
    },

    init : function(amlNode){
        this.panel = winDbgStack;
        this.rightPane = colRight;
        this.nodes.push(this.rightPane.appendChild(winDbgStack));

        this.paths = {};
        var _self = this;
        mdlDbgSources.addEventListener("afterload", function() {
            _self.$syncTree();
        });
        mdlDbgSources.addEventListener("update", function(e) {
            if (e.action != "add")
                return;
            // TODO: optimize this!
            _self.$syncTree();
        });
        fs.model.addEventListener("update", function(e) {
            if (e.action != "insert")
                return;
            // TODO: optimize this!
            _self.$syncTree();
        });
        //@todo move this to noderunner...
        dbg.addEventListener("changeframe", function(e) {
            e.data && _self.showDebugFile(e.data.getAttribute("scriptid"));
        });

        lstBreakpoints.addEventListener("afterselect", function(e) {
            if (e.selected && e.selected.getAttribute("scriptid"))
                _self.showDebugFile(e.selected.getAttribute("scriptid"), parseInt(e.selected.getAttribute("line")) + 1);
            // TODO sometimes we don't have a scriptID
        });

        ide.addEventListener("afterfilesave", function(e) {
            var node = e.node;
            var doc = e.doc;
            
            var scriptId = node.getAttribute("scriptid");
            if (!scriptId)
                return;
                
            var value = e.value || doc.getValue();
            var NODE_PREFIX = "(function (exports, require, module, __filename, __dirname) { "
            var NODE_POSTFIX = "\n});";
            dbg.changeLive(scriptId, NODE_PREFIX + value + NODE_POSTFIX, false, function(e) {
                //console.log("v8 updated", e);
            });
        })
    },

    showDebugFile : function(scriptId, row, column, text) {
        var file = fs.model.queryNode("//file[@scriptid='" + scriptId + "']");

        if (file) {
            editors.jump(file, row, column, text, null, true);
        } else {
            var script = mdlDbgSources.queryNode("//file[@scriptid='" + scriptId + "']");
            if (!script)
                return;

            var name = script.getAttribute("scriptname");
            var value = name.split("/").pop();

            if (name.indexOf(ide.workspaceDir) == 0) {
                var path = ide.davPrefix + name.slice(ide.workspaceDir.length);
                // TODO this has to be refactored to support multiple tabs
                var page = tabEditors.getPage(path);
                if (page)
                    var node = page.xmlRoot;
                else {
                    var node = apf.n("<file />")
                        .attr("name", value)
                        .attr("path", path)
                        .attr("contenttype", "application/javascript")
                        .attr("scriptid", script.getAttribute("scriptid"))
                        .attr("scriptname", script.getAttribute("scriptname"))
                        .attr("lineoffset", "0").node();
                }
                editors.jump(node, row, column, text, null, page ? true : false);
            }
            else {
                var page = tabEditors.getPage(value);
                if (page)
                    editors.jump(page.xmlRoot, row, column, text, null, true);
                else {
                    var node = apf.n("<file />")
                        .attr("name", value)
                        .attr("path", name)
                        .attr("contenttype", "application/javascript")
                        .attr("scriptid", script.getAttribute("scriptid"))
                        .attr("scriptname", script.getAttribute("scriptname"))
                        .attr("debug", "1")
                        .attr("lineoffset", "0").node();

                    dbg.loadScript(script, function(source) {
                        var doc = ide.createDocument(node, source);
                        editors.jump(node, row, column, text, doc);
                    });
                }
            }
        }
    },

    count : 0,
    $syncTree : function() {
        if (this.inSync) return;
        this.inSync = true;
        var dbgFiles = mdlDbgSources.data.childNodes;

        var workspaceDir = ide.workspaceDir;
        for (var i=0,l=dbgFiles.length; i<l; i++) {
            var dbgFile = dbgFiles[i];
            var name = dbgFile.getAttribute("scriptname");
            if (name.indexOf(workspaceDir) != 0)
                continue;
            this.paths[name] = dbgFile;
        }
        var treeFiles = fs.model.data.getElementsByTagName("file");
        var tabFiles = ide.getAllPageModels();
        var files = tabFiles.concat(Array.prototype.slice.call(treeFiles, 0));

        var davPrefix = ide.davPrefix;
        for (var i=0,l=files.length; i<l; i++) {
            var file = files[i];
            var path = file.getAttribute("scriptname");

            var dbgFile = this.paths[path];
            if (dbgFile)
                apf.b(file).attr("scriptid", dbgFile.getAttribute("scriptid"));
        }
        this.inSync = false;
    },

    enable : function(){
        panels.initPanel(this);
        
        this.nodes.each(function(item){
            if (item.show)
                item.show();
        });
        this.rightPane.setProperty("visible", true);

        //Quick Fix
        if (apf.isGecko)
            apf.layout.forceResize(ide.vbMain.$ext);
    },

    disable : function(){
        if (!this.panel)
            return;
        
        this.nodes.each(function(item){
            if (item.hide)
                item.hide();
        });
        this.rightPane.setProperty("visible", false);
        //log.disable(true);

        //Quick Fix
        if (apf.isGecko)
            apf.layout.forceResize(ide.vbMain.$ext);
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });
        winV8.destroy(true, true);
        this.$layoutItem.destroy(true, true);

        this.nodes = [];
        
        panels.unregister(this);
    }
});

});
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var fs = require("ext/filesystem/filesystem");
var css = require("text!ext/save/save.css");
var menus = require("ext/menus/menus");
var markup = require("text!ext/save/save.xml");
var commands = require("ext/commands/commands");

var SAVING = 0;
var SAVED = 1;
var OFFLINE = 2;

module.exports = ext.register("ext/save/save", {
    dev         : "Ajax.org",
    name        : "Save",
    alone       : true,
    type        : ext.GENERAL,
    markup      : markup,
    css         : css,
    deps        : [fs],
    offline     : true,

    nodes       : [ ],
    saveBuffer  : {},

    hook : function(){
        var _self = this;

        commands.addCommand({
            name: "quicksave",
            hint: "save the currently active file to disk",
            bindKey: {mac: "Command-S", win: "Ctrl-S"},
            isAvailable : function(editor){
                return !!editor;
            },
            exec: function () {
                _self.quicksave();
            }
        });

        commands.addCommand({
            name: "saveas",
            hint: "save the file to disk with a different filename",
            bindKey: {mac: "Command-Shift-S", win: "Ctrl-Shift-S"},
            isAvailable : function(editor){
                return !!editor;
            },
            exec: function () {
                _self.saveas();
            }
        });

        commands.addCommand({
            name: "saveall",
            hint: "downgrade the currently active file to the last saved version",
            isAvailable : function(editor){
                return !!editor;
            },
            exec: function () {
                _self.saveall();
            }
        });

        commands.addCommand({
            name: "reverttosaved",
            bindKey: {mac: "Ctrl-Shift-Q", win: "Ctrl-Shift-Q"},
            isAvailable : function(editor){
                return !!editor;
            },
            exec: function () {
                _self.reverttosaved();
            }
        });

        ide.addEventListener("init.ext/editors/editors", function(){
            tabEditors.addEventListener("close", _self.$close = function(e) {
                var at = e.page.$at;
                var node = e.page.$doc.getNode();

                if (node.getAttribute("deleted"))
                    return;

                if (node && (at && at.undo_ptr && at.$undostack[at.$undostack.length-1] !== at.undo_ptr
                  || !at.undo_ptr && node.getAttribute("changed") == 1)
                  && (!node.getAttribute("newfile") || e.page.$doc.getValue())) {
                    ext.initExtension(_self);

                    if (ide.dispatchEvent("beforesavewarn", {
                        page : e.page,
                        doc  : e.page.$doc
                    }) === false)
                        return;

                    var pages   = tabEditors.getPages(),
                    currIdx = pages.indexOf(e.page);
                    tabEditors.set(pages[currIdx].id); //jump to file

                    var filename = node.getAttribute("path").replace(ide.workspaceDir, "").replace(ide.davPrefix, "");

                    winCloseConfirm.page = e.page;
                    winCloseConfirm.all  = -100;
                    winCloseConfirm.show();

                    fileDesc.replaceMarkup("<div><h3>Save " + apf.escapeXML(filename) + "?</h3><div>This file has unsaved changes. Your changes will be lost if you don't save them.</div></div>", {"noLoadingMsg": false});

                    winCloseConfirm.addEventListener("hide", function(){
                        if (winCloseConfirm.all != -100) {
                            var f = function(resetUndo){
                                var page;
                                if (!(page=winCloseConfirm.page))
                                    return;
                                delete winCloseConfirm.page;
                                delete page.noAnim;

                                page.dispatchEvent("aftersavedialogclosed");

                                tabEditors.remove(page, true, page.noAnim);
                                if (resetUndo)
                                    page.$at.undo(-1);
                            };

                            if (winCloseConfirm.all == -200)
                                _self.quicksave(winCloseConfirm.page, f);
                            else
                                f(true);
                            /*winSaveAs.page = winCloseConfirm.page;*/
                        }
                        else
                            tabEditors.dispatchEvent("aftersavedialogcancel");

                        winCloseConfirm.removeEventListener("hide", arguments.callee);
                    });

                    btnYesAll.hide();
                    btnNoAll.hide();

                    e.preventDefault();
                }
            }, true);
        });

        this.nodes.push(
            menus.$insertByIndex(barTools, new apf.button({
                id       : "btnSave",
                icon     : "save.png",
                caption  : "Save",
                tooltip  : "Save",
                skin     : "c9-toolbarbutton-glossy",
                disabled : "{!!!tabEditors.activepage}",
                command  : "quicksave"
            }), 1000)
        );

        var saveItem, saveAsItem, itmRevertToSaved;
        this.nodes.push(
            saveItem = menus.addItemByPath("File/Save", new apf.item({
                command : "quicksave",
                disabled : "{!!!tabEditors.activepage}"
            }), 1000),

            saveAsItem = menus.addItemByPath("File/Save As...", new apf.item({
                command : "saveas",
                disabled : "{!!!tabEditors.activepage}"
            }), 1100),

            menus.addItemByPath("File/Save All", new apf.item({
                command : "saveall",
                disabled : "{!!!tabEditors.activepage}"
            }), 1200),

            itmRevertToSaved = menus.addItemByPath("File/Revert to Saved", new apf.item({
                command : "reverttosaved",
                disabled : "{!!!tabEditors.activepage}"
            }), 700)
        );

        ide.addEventListener("afterreload", function(e){
            var doc = e.doc;
            var at = doc.$page.$at;

            at.addEventListener("afterchange", function(){
                at.undo_ptr = at.$undostack[at.$undostack.length-1];
                apf.xmldb.removeAttribute(doc.getNode(), "changed");
            });
        });

        // when we're going offline we'll disable the UI
        ide.addEventListener("afteroffline", function() {
            _self.disable();
        });

        // when we're back online we'll first update the UI and then trigger an autosave if enabled
        ide.addEventListener("afteronline", function() {
            _self.enable();
        });

        // if we retrieve an actual 'real' file save event then we'll set the UI state to 'saving'
        ide.addEventListener("fs.beforefilesave", function () {
            _self.setUiStateSaving();
        });

        // and afterwards we'll show 'SAVED' or 'NOT SAVED' depending on whether it succeeded
        ide.addEventListener("fs.afterfilesave", function (e) {
            if (e.success) {
                _self.setUiStateSaved();
            }
            else {
                _self.setUiStateOffline();
            }
        });
    },

    init : function(amlNode){
        this.fileDesc = winCloseConfirm.selectSingleNode("a:vbox");

        apf.importCssString((this.css || ""));
        winCloseConfirm.onafterrender = function(){
            btnYesAll.addEventListener("click", function(){
                winCloseConfirm.all = 1;
                winCloseConfirm.hide();
            });
            btnNoAll.addEventListener("click", function(){
                winCloseConfirm.all = -1;
                winCloseConfirm.hide();
            });
            btnSaveYes.addEventListener("click", function(){
                winCloseConfirm.all = -200;
                winCloseConfirm.hide();
            });
            btnSaveNo.addEventListener("click", function(){
                winCloseConfirm.all = 0;
                winCloseConfirm.hide();
            });
            btnSaveCancel.addEventListener("click", function(){
                winCloseConfirm.all = -100;
                winCloseConfirm.hide();
            });
        };

        winSaveAs.addEventListener("hide", function(){
            if (winSaveAs.page) {
                tabEditors.remove(winSaveAs.page, true);
                winSaveAs.page.$at.undo(-1);
                delete winSaveAs.page;
            }
        });

        trSaveAs.addEventListener("beforerename", this.$beforerename = function(e){
            if (!ide.onLine && !ide.offlineFileSystemSupport) return false;

            if (trSaveAs.$model.data.firstChild == trSaveAs.selected)
                return false;

            // check for a path with the same name, which is not allowed to rename to:
            var path = e.args[0].getAttribute("path"),
                newpath = path.replace(/^(.*\/)[^\/]+$/, "$1" + e.args[1]).toLowerCase();

            var exists, nodes = trSaveAs.getModel().queryNodes(".//node()");
            for (var i = 0, len = nodes.length; i < len; i++) {
                var pathLwr = nodes[i].getAttribute("path").toLowerCase();
                if (nodes[i] != e.args[0] && pathLwr === newpath) {
                    exists = true;
                    break;
                }
            }

            if (exists) {
                util.alert("Error", "Unable to Rename",
                    "That name is already taken. Please choose a different name.");
                trSaveAs.getActionTracker().undo();
                return false;
            }

            fs.beforeRename(e.args[0], e.args[1]);
        });
    },

    reverttosaved : function(){
        ide.dispatchEvent("reload", {doc : ide.getActivePage().$doc});
    },

    saveall : function() {
        // previous version of this function used
        // .forEach(this.quicksave), but that also passes the index parameter (2nd one)
        // of forEach to the quicksave function.
        var _self = this;
        tabEditors.getPages().forEach(function (page) {
            _self.quicksave(page);
        });
    },

    saveAllInteractive : function(pages, callback){
        ext.initExtension(this);

        winCloseConfirm.all = 0;

        var _self = this;
        apf.asyncForEach(pages, function(item, next) {
            var at = item.$at;
            if (at.undo_ptr && at.$undostack[at.$undostack.length-1] !== at.undo_ptr) {
                if (winCloseConfirm.all == 1)
                    _self.quicksave(item);

                if (winCloseConfirm.all)
                    return next();

                tabEditors.set(item);
                winCloseConfirm.page = item;
                winCloseConfirm.show();
                winCloseConfirm.addEventListener("hide", function(){
                    if (winCloseConfirm.all == 1)
                        _self.quicksave(item);

                    winCloseConfirm.removeEventListener("hide", arguments.callee);
                    next();
                });

                btnYesAll.setProperty("visible", pages.length > 1);
                btnNoAll.setProperty("visible", pages.length > 1);
            }
            else
                next();
        },
        function() {
            callback(winCloseConfirm.all);
        });
    },

    ideIsOfflineMessage: function() {
        util.alert("Could not save",
            "Please check your connection",
            "When your connection has been restored you can try to save the file again.");
    },

    // `silentsave` indicates whether the saving of the file is forced by the user or not.
    quicksave : function(page, callback, silentsave) {
        if (!page || !page.$at)
            page = ide.getActivePage();

        if (!page)
            return;

        if (typeof callback !== "function") {
            callback = null;
        }

        var corrected = ide.dispatchEvent("correctactivepage", {page: page});
        if (corrected)
            page = corrected;

        var doc  = page.$doc;
        var node = doc.getNode();
        var path = node.getAttribute("path");

        if (node.getAttribute("debug"))
            return;

        if (ide.dispatchEvent("beforefilesave", {node: node, doc: doc }) === false)
            return;

        if (node.getAttribute("newfile") && !node.getAttribute("cli") && !node.getAttribute("ignore") !== "1"){
            this.saveas(page, callback);
            return;
        }

        if (!ide.onLine) {
            return this.ideIsOfflineMessage();
        }

        var _self = this;

        var value = doc.getValue();

        // so we had this edge case where the doc is already destroyed but we still fire
        // and undefined was returned.
        // To prevent file emptying, we'll add an extra check here.
        if (typeof value === "undefined")
            return;

        // check if we're already saving!
        var saving = parseInt(node.getAttribute("saving"), 10);
        if (saving) {
            this.saveBuffer[path] = page;
            return;
        }

        apf.xmldb.setAttribute(node, "saving", "1");

        if (callback) {
            ide.addEventListener("afterfilesave", function(e) {
                if (e.node == node) {
                    callback();
                    ide.removeEventListener("afterfilesave", arguments.callee);
                }
            });
        }

        // raw fs events
        ide.dispatchEvent("fs.beforefilesave", { path: path });

        fs.saveFile(path, value, function(data, state, extra){

            ide.dispatchEvent("track_action", {
                type: "save as filetype",
                fileType: node.getAttribute("name").split(".").pop().toLowerCase(),
                success: state != apf.SUCCESS ? "false" : "true"
            });
            apf.xmldb.removeAttribute(node, "saving");

            ide.dispatchEvent("fs.afterfilesave", { path: path, success: state == apf.SUCCESS });

            if (state != apf.SUCCESS) {
                // ide is not online, or away??
                if (!ide.onLine || ide.connection.away) {
                    // if we're doing a silent save, we'll ignore it
                    // because its a not user triggered action
                    if (silentsave) {
                        return;
                    }
                    // otherwise we show a message that saving failed unfortunately
                    return _self.ideIsOfflineMessage();
                }

                // all other error cases
                return util.alert(
                    "Could not save document",
                    "An error occurred while saving this document",
                    "Please see if your internet connection is available and try again. "
                        + (state == apf.TIMEOUT
                            ? "The connection timed out."
                            : "The error reported was " + extra.message));
            }

            page.$at.dispatchEvent("afterchange");

            ide.dispatchEvent("afterfilesave", {
                node: node,
                doc: doc,
                value: value,
                oldpath: path,
                silentsave: silentsave
            });

            apf.xmldb.removeAttribute(node, "new");
            apf.xmldb.removeAttribute(node, "changed");
            apf.xmldb.setAttribute(node, "modifieddate", apf.queryValue(extra.data, "//d:getlastmodified"));

            if (_self.saveBuffer[path]) {
                delete _self.saveBuffer[path];
                _self.quicksave(page);
            }
        });

        var at = page.$at;
        at.undo_ptr = at.$undostack[at.$undostack.length-1];
        return false;
    },

    _saveAsNoUI: function(page, path, newPath, isReplace) {
        if (!page || !path)
            return;

        newPath = newPath || path;

        var file = page.$model.data;
        var oldFile = file;
        var oldPath = oldFile.getAttribute("path");
        var saving = parseInt(file.getAttribute("saving"), 10);

        if (saving) {
            this.saveBuffer[path] = page;
            return;
        }
        apf.xmldb.setAttribute(file, "saving", "1");

        var _self = this;
        var value = page.$doc.getValue();
        fs.saveFile(newPath, value, function(value, state, extra) {
            if (state !== apf.SUCCESS) {
                apf.xmldb.removeAttribute(file, "saving")
                return util.alert("Could not save document",
                    "An error occurred while saving this document",
                    "Please see if your internet connection is available and try again." +
                    "The server responded with status " + extra.status + ".");
            }

            var model = page.$model;
            var node = model.getXml();
            var doc = page.$doc;

            if (path !== newPath || parseInt(node.getAttribute("newfile") || 0, 10) === 1) {
                file = apf.getCleanCopy(node);
                fs.beforeRename(file, null, newPath, false, isReplace);
                doc.setNode(file);
                model.load(file);
                tabEditors.set(ide.getActivePage());
            }

            apf.xmldb.removeAttribute(oldFile, "saving");
            apf.xmldb.removeAttribute(file, "saving");

            if (_self.saveBuffer[path]) {
                delete _self.saveBuffer[path];
                _self._saveAsNoUI(page);
            }

            if (parseInt(file.getAttribute("newfile") || "0", 10) === 1) {
                apf.xmldb.removeAttribute(file, "newfile");
                apf.xmldb.removeAttribute(file, "changed");
                var xpath = newPath.replace(new RegExp("\/" + window.cloud9config.davPrefix.split("/")[1]), "")
                                    .replace(new RegExp("\/" + file.getAttribute("name")), "")
                                    .replace(/\/([^/]*)/g, "/node()[@name=\"$1\"]")
                                    .replace(/\/node\(\)\[@name="workspace"\]/, "")
                                    .replace(/\//, "") || "node()";
                if (self.trFiles && xpath) {
                    var oNode = trFiles.queryNode(xpath);
                    if (oNode && !trFiles.queryNode('//node()[@path=' + util.escapeXpathString(newPath) + ']'))
                        apf.xmldb.appendChild(oNode, file);
                }
            }

            ide.dispatchEvent("afterfilesave", {
                node: node,
                doc: doc,
                value: value,
                oldpath: oldPath,
                silentsave: false // It is a forced save, comes from UI
            });
        });

        var at = page.$at
        at.undo_ptr = at.$undostack[at.$undostack.length-1];
        page.$at.dispatchEvent("afterchange", {
            newPath: newPath
        });
    },

    choosePath : function(path, select) {
        fs.list((path.match(/(.*)\/[^/]*/) || {})[1] || path, function (data, state, extra) {
            if (new RegExp("<folder.*" + path + ".*>").test(data)) {
                path  = path.replace(new RegExp("\/" + window.cloud9config.davPrefix.split("/")[1]), "")
                            .replace(/\/([^/]*)/g, "/node()[@name=\"$1\"]")
                            .replace(/\/node\(\)\[@name="workspace"\]/, "")
                            .replace(/\//, "");

                trSaveAs.expandList([path], function() {
                    var node = trSaveAs.getModel().data.selectSingleNode(path);
                    trSaveAs.select(node);
                });
            }
        });
    },

    // Function called from the 'Save As' menu dialog, and from the C9 CLI.
    // It saves a file with a different name, involving UI.
    saveas : function(page, callback){
        if (!page || !page.$at)
            page = ide.getActivePage();

        if (!page)
            return;

        var path = page ? page.$model.data.getAttribute("path") : false;
        if (!path)
            return;

        ext.initExtension(this);

        if (callback) {
            var doc = page.$doc;
            ide.addEventListener("afterfilesave", function(e){
                if (e.doc == doc) {
                    callback();
                    ide.removeEventListener("afterfilesave", arguments.callee);
                }
            });
        }

        var fooPath = path.split("/");
        txtSaveAs.setValue(fooPath.pop());
        lblPath.setProperty("caption", fooPath.join("/") + "/");
        winSaveAs.show();
    },

    // Called by the UI 'confirm' button in winSaveAs.
    confirmSaveAs : function(page) {
        page = page || ide.getActivePage();
        var file = page.$model.data;
        var path = file.getAttribute("path");
        var newPath = lblPath.getProperty("caption") + txtSaveAs.getValue();

        var isReplace = false;
        // check if we're already saving!
        var saving = parseInt(file.getAttribute("saving"), 10);
        if (saving) {
            this.saveBuffer[path] = page;
            return;
        }

        //apf.xmldb.setAttribute(file, "saving", "1");

        var _self = this;
        var doSave = function() {
            winSaveAs.hide();
            _self._saveAsNoUI(page, path, newPath, isReplace);

            if (window.winConfirm) {
                winConfirm.hide();

                if (window.btnConfirmOk && btnConfirmOk.caption == "Yes")
                    btnConfirmOk.setCaption("Ok");
            }
        };

        var doCancel = function() {
            if (window.winConfirm && btnConfirmOk.caption == "Yes")
                btnConfirmOk.setCaption("Ok");
        };
        if (path !== newPath || parseInt(file.getAttribute("newfile") || 0, 10) === 1) {
            fs.exists(newPath, function (exists) {
                if (exists) {
                    var name = newPath.match(/\/([^/]*)$/)[1];

                    isReplace = true;
                    util.confirm(
                        "A file with this name already exists",
                        "\"" + name + "\" already exists, do you want to replace it?",
                        "A file with the same name already exists at this location." +
                        "Selecting Yes will overwrite the existing document.",
                        doSave,
                        doCancel);
                    btnConfirmOk.setCaption("Yes");
                }
                else {
                    doSave();
                }
            });
        }
        else {
            doSave();
        }
    },

    expandTree : function(){
        var _self = this;

        function expand(){
            var tabPage = ide.getActivePage(),
                path    = tabPage ? tabPage.$model.data.getAttribute('path') : false,
                isNew   = tabPage ? tabPage.$model.data.getAttribute('newfile') : false;
            if (!isNew)
                _self.choosePath(path);
            else
                trSaveAs.slideOpen(null, trSaveAs.getModel().data.selectSingleNode('//folder'));
        }

        if (trSaveAs.getModel()) {
            expand();
        }
        else {
            trSaveAs.addEventListener("afterload", expand);
        }
    },

    renameFile : function(node) {
        var path = node.getAttribute("path");
        var oldpath = node.getAttribute("oldpath");
        davProject.rename(oldpath, path, true, false, function(data, state, extra) {
            if (state !== apf.SUCCESS) {
                // TODO: revert the rename!!
                return;
            }

            ide.dispatchEvent("afterupdatefile", {
                path: oldpath,
                newPath: path,
                xmlNode: node,
                isFolder: node.getAttribute("type") === "folder"
            });
        });
    },

    /**
     * Set the UI state to 'saving'
     */
    setUiStateSaving: function () {
        btnSave.show();

        apf.setStyleClass(btnSave.$ext, "saving", ["saved", "error"]);
        apf.setStyleClass(saveStatus, "saving", ["saved"]);
        btnSave.currentState = SAVING;
        btnSave.setCaption("Saving");
    },

    $uiStateSavedTimeout: null,
    /**
     * Set the UI state to 'saved'
     */
    setUiStateSaved: function () {
        var _self = this;

        btnSave.show();

        apf.setStyleClass(btnSave.$ext, "saved", ["saving", "error"]);
        apf.setStyleClass(saveStatus, "saved", ["saving"]);
        btnSave.currentState = SAVED;
        btnSave.setCaption("Changes saved");

        // after 4000 ms. we'll hide the label (clear old timeout first)
        if (_self.$uiS$uiStateSavedTimeout)
            clearTimeout(_self.$ui$uiStateSavedTimeout);

        _self.$uiStateSavedTimeout = setTimeout(function () {
            if (btnSave.currentState === SAVED) {
                btnSave.hide();
            }
        }, 4000);
    },

    setUiStateOffline: function () {
        btnSave.show();

        // don't blink!
        apf.setStyleClass(btnSave.$ext, "saved");
        apf.setStyleClass(btnSave.$ext, "error", ["saving"]);

        btnSave.currentState = OFFLINE;
        btnSave.setCaption("Not saved");
    },

    destroy : function(){
        menus.remove("File/~", 1100);
        menus.remove("File/Save All");
        menus.remove("File/Save As...");
        menus.remove("File/Save");
        menus.remove("File/~", 800);
        menus.remove("File/Revert to Saved");

        commands.removeCommandsByName(
            ["quicksave", "saveas", "saveall", "reverttosaved"]);

        tabEditors.removeEventListener("close", this.$close, true);
        this.$destroy();
    }
});

});
Example #27
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var settings = require("core/settings");
var commands = require("ext/commands/commands");
var main = require("ext/main/main");
var anims = require("ext/anims/anims");

module.exports = ext.register("ext/menus/menus", {
    name    : "Menus",
    dev     : "Ajax.org",
    alone   : true,
    type    : ext.GENERAL,
    deps    : [ main ],
    nodes   : [],
    items   : {},
    menus   : {},
    count   : 0,
    debug   : location.href.indexOf('menus=1') > -1,
    minimized : false,

    init : function(){
        var _self = this;

        this.nodes.push(
            this.menubar = logobar.insertBefore(new apf.bar({
                "class" : "fakehbox aligncenter",
                style : "padding : 0 5px 0 5px;position:static",
            }), logobar.firstChild),

//            this.setRootMenu("Workspace", 10),
            this.setRootMenu("File", 100),
            this.setRootMenu("Edit", 200),
            this.setRootMenu("Selection", 300),
            this.setRootMenu("Find", 400),
            this.setRootMenu("View", 500),
            this.setRootMenu("Goto", 600),
            this.setRootMenu("Tools", 700)
        );

        var timer;
        this.menubar.insertBefore(new apf.button({
            "class" : "c9-mbar-minimize",
            "skin" : "c9-simple-btn",
            "onclick" : function(e){
                if (!_self.minimized)
                    _self.minimize();
                else
                    _self.restore();
            }
        }), this.menubar.firstChild);

        logobar.$ext.addEventListener("mousedown", function(){
            _self.restore();
        });

        var logoCorner = document.querySelector(".c9-mbar-round");

        logobar.$ext.addEventListener("mouseover",function(e){
            if (!_self.minimized || !ide.inited
              || apf.isChildOf(logobar.$ext, e.fromElement, true)
              || apf.isChildOf(logoCorner, e.toElement, true))
                return;

            clearTimeout(timer);
            timer = setTimeout(function(){
                _self.restore(true);
            }, 500);
        });
        logobar.$ext.addEventListener("mouseout",function(e){
            if (!_self.minimized || !ide.inited
              || apf.isChildOf(logobar.$ext, e.toElement, true))
                return;

            clearTimeout(timer);
            if (apf.popup.isShowing(apf.popup.last)) {
                timer = setTimeout(function(){
                    if (apf.popup.isShowing(apf.popup.last))
                        timer = setTimeout(arguments.callee, 500);
                    else
                        _self.minimize(true);
                }, 500);
            }
            else {
                timer = setTimeout(function(){
                    _self.minimize(true);
                }, 500);
            }
        });

        ide.addEventListener("settings.load", function(e){
            e.ext.setDefaults("auto/menus", [["minimized", "false"]]);

            if (apf.isTrue(e.model.queryValue("auto/menus/@minimized"))) {
                _self.minimize(true, true);
                _self.minimized = true;
            }
        });

        this.addItemByPath("View/~", new apf.divider(), 9999);

        apf.button.prototype.$propHandlers["hotkey"] = function(value){
            if (this.$hotkey)
                apf.setNodeValue(this.$hotkey, apf.isMac
                      ? apf.hotkeys.toMacNotation(this.hotkey) : this.hotkey);

            if (this.tooltip)
                apf.GuiElement.propHandlers.tooltip.call(this, this.tooltip);
        }

        apf.item.prototype.$propHandlers["hotkey"] = function(value){
            if (!this.$amlLoaded) {
                var _self = this;
                this.addEventListener("DOMNodeInsertedIntoDocument", function(e){
                    if (_self.$hotkey && _self.hotkey)
                        apf.setNodeValue(this.$hotkey, apf.isMac
                          ? apf.hotkeys.toMacNotation(this.hotkey) : this.hotkey);
                });
            }
            else if (this.$hotkey)
                apf.setNodeValue(this.$hotkey,
                    apf.isMac ? apf.hotkeys.toMacNotation(value) : value);
        }

        apf.splitbutton.prototype.$propHandlers["command"] =
        apf.button.prototype.$propHandlers["command"] =
        apf.item.prototype.$propHandlers["command"] = function(value){
            if (!value) {
                this.removeAttribute("hotkey");
                this.onclick = null;
                return;
            }

            this.setAttribute("hotkey",
                value && "{ide.commandManager." + value + "}" || "");

            this.onclick = function(){
                commands.exec(value, null, {source: "click"});
                var command = commands.commands[value];
                if (command && command.focusContext)
                    self["req"+"uire"]("ext/editors/editors").currentEditor.focus();
            } || null;
        }

        //update c9 main logo link
        if(window.cloud9config.hosted) {
            var mainlogo = logobar.$ext.getElementsByClassName('mainlogo');
            if(mainlogo && (mainlogo = mainlogo[0])) {
                mainlogo.title = "back to dashboard";
                mainlogo.href = "/dashboard.html";
                mainlogo.innerHTML = "Dashboard";
            }
        }
    },

    $insertByIndex : function(parent, item, index) {
        item.$position = index;

        var beforeNode, diff = 100000000, nodes = parent.childNodes;
        for (var i = 0, l = nodes.length; i < l; i++) {
            var d = nodes[i].$position - index;
            if (d > 0 && d < diff) {
                beforeNode = nodes[i];
                diff = d;
            }
        }

        return parent.insertBefore(item, beforeNode);
    },

    $checkItems : function(e){
        if (e.value) {
            var page = self.tabEditors && tabEditors.$activepage;
            var editor = page && page.$editor;

            var nodes = this.childNodes;
            for (var a, cmd, n, i = nodes.length - 1; i >= 0; i--) {
                if (a = ((cmd = (n = nodes[i]).command)
                  && commands.commands[cmd].isAvailable) || n.isAvailable)
                    n[a(editor) ? "enable" : "disable"]();
            }
        }
    },

    setRootMenu : function(name, index, item, menu){
        var items = this.items, menus = this.menus;

       if (menu) {
            if (!menu.id)
                menu.setAttribute("id", "mnuMenus" + ++this.count);
            menus[name] = menu;
        }
        else {
            menu = menus[name];
            if (!menu) {
                menu = menus[name] = new apf.menu({
                    id : "mnuMenus" + ++this.count,
                    "onprop.visible" : this.$checkItems
                });
            }
        }

        if (item) {
            item.setAttribute("submenu", menu.id || "mnuMenus" + this.count);
            items[name] = item;
        }
        else {
            item = items[name];
            if (!item) {
                item = items[name] = new apf.button({
                    skin    : "c9-menu-btn",
                    submenu : menu.id,
                    margin  : "0 0 0 0",
                    caption : (this.debug ? "\\[" + index + "\\] " : "") + name
                });
            }
        }

        if (typeof index == "number")
            this.$insertByIndex(this.menubar, item, index);

        return menu;
    },

    setSubMenu : function(parent, name, index, item, menu){
        var items = this.items, menu, menus = this.menus, item;

        if (menu) {
            //Switch old menu for new menu
            if (menus[name]) {
                var nodes = menus[name].childNodes;
                for (var i = 0, l = nodes.length; i < l; i++) {
                    this.$insertByIndex(menu, nodes[i], nodes[i].$position);
                }

                menus[name].destroy(true, true);
            }

            if (!menu.id)
                menu.setAttribute("id", "mnuMenus" + ++this.count);
            menus[name] = menu;
        }
        else {
            menu = menus[name];
            if (!menu) {
                menu = menus[name] = new apf.menu({
                    id : "mnuMenus" + ++this.count,
                    "onprop.visible" : this.$checkItems
                });
            }
        }

        if (item) {
            item.setAttribute("submenu", menu.id);
            item.setAttribute("caption",
                apf.escapeXML((this.debug ? "[" + index + "]" : "") + name.split("/").pop()));
            items[name] = item;
        }
        else {
            item = items[name];
            if (!item) {
                item = items[name] = new apf.item({
                    submenu : menu.id,
                    caption : (this.debug ? "\\[" + index + "\\] " : "") +
                        name.split("/").pop()
                });
            }
            else {
                item.setAttribute("submenu", menu.id);
            }
        }

        if (typeof index == "number")
            this.$insertByIndex(parent, item, index);

        return menu;
    },

    setMenuItem : function(parent, name, menuItem, index, item){
        var items = this.items;
        var itemName = name.split("/").pop();
        if (itemName == "~")
            name += index;

        item = items[name];
        if (!item) {
            item = items[name] = menuItem;

            if (itemName != "~")
                item.setAttribute("caption",
                    apf.escapeXML((this.debug ? "\\[" + index + "\\] " : "") + itemName));
        }

        //index...
        if (typeof index == "number")
            this.$insertByIndex(parent, item, index);
        else
            parent.appendChild(item);
    },

    addItemByPath : function(path, menuItem, index, menu){
        var steps = path.split("/"), name, p = [], isLast, menus = this.menus;
        var curpath;

        if (!menuItem)
            menuItem = "";

        for (var i = 0, l = steps.length; i < l; i++) {
            name = steps[i];
            p.push(name);
            curpath = p.join("/");

            //Menubar button & menu
            if (i == 0 && !menu) {
                isLast = !steps[i + 1];
                menu = !isLast && menus[curpath]
                  || this.setRootMenu(name, i == l - 1  || isLast ? index : null,
                    isLast && (!menuItem.nodeFunc && menuItem.item || menuItem.localName == "button" && menuItem),
                    isLast && (!menuItem.nodeFunc && menuItem.menu || menuItem.localName == "menu" && menuItem));
            }
            //Submenu item & menu
            else if (i != l - 1 || !menuItem.nodeFunc) {
                isLast = !steps[i + 1];
                menu = !isLast && menus[curpath]
                  || this.setSubMenu(menu, curpath, i == l - 1 || isLast ? index : null,
                    isLast && (!menuItem.nodeFunc ? menuItem.item : menuItem.localName != "menu" && menuItem),
                    isLast && (!menuItem.nodeFunc && menuItem.menu || menuItem.localName == "menu" && menuItem));
            }
            //Leaf
            else {
                this.setMenuItem(menu, p.join("/"), menuItem, index);
            }

            if (isLast) break;
        }

        return menuItem || menu;
    },

    addItemToMenu : function(menu, menuItem, index){
        this.$insertByIndex(menu, menuItem, index);
    },

    enableItem : function(path){

    },

    disableItem : function(path){

    },

    remove : function(path) {
        //menus.remove("Tools/Beautify Selection");
    },

    getMenuId : function(path){
        var menu = this.menus[path];
        return menu && menu.id;
    },

    restore : function(preview){
        apf.setStyleClass(logobar.$ext, "", ["minimized"]);

        logobar.$ext.style.overflow = "hidden";

        anims.animateSplitBoxNode(logobar, {
            height: "31px",
            timingFunction: "cubic-bezier(.10, .10, .25, .90)",
            duration: 0.2
        }, function(){
            apf.layout.forceResize(tabEditors.$ext);
            logobar.$ext.style.overflow = "";
        });

        if (!preview) {
            settings.model.setQueryValue("auto/menus/@minimized", "false");
            this.minimized = false;

            ide.dispatchEvent("menus.restore");
        }
    },

    minimize : function(preview, noAnim){
        logobar.$ext.style.overflow = "hidden";

        anims.animateSplitBoxNode(logobar, {
            height: "12px",
            timingFunction: "cubic-bezier(.10, .10, .25, .90)",
            duration: 0.2,
            immediate: noAnim
        }, function(){
            apf.setStyleClass(logobar.$ext, "minimized");
            apf.layout.forceResize();
            logobar.$ext.style.overflow = "";
        });

        if (!preview) {
            settings.model.setQueryValue("auto/menus/@minimized", "true");
            this.minimized = true;

            ide.dispatchEvent("menus.minimize");
        }
    },

    enable : function(){
        this.hbox.show();
        //this.splitter.show();
    },

    disable : function(){
        this.hbox.hide();
        //this.splitter.hide();
    },

    destroy : function(){
        this.hbox.destroy(true, true);
        this.$destroy();
        //this.splitter.destroy(true, true);
    }
});

});
Example #28
0
File: code.js Project: alyx/cloud9
define(function(require, exports, module) {

require("apf/elements/codeeditor");

var ide = require("core/ide");
var ext = require("core/ext");
var EditSession = require("ace/edit_session").EditSession;
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
var useragent = require("ace/lib/useragent");
var Document = require("ace/document").Document;
var Range = require("ace/range").Range;
var ProxyDocument = require("ext/code/proxydocument");
var CommandManager = require("ace/commands/command_manager").CommandManager;
var defaultCommands = require("ace/commands/default_commands").commands;
var markup = require("text!ext/code/code.xml");
var settings = require("ext/settings/settings");
var markupSettings = require("text!ext/code/settings.xml");
var editors = require("ext/editors/editors");

apf.actiontracker.actions.aceupdate = function(undoObj, undo){
    var q = undoObj.args;

    if (!undoObj.initial) {
        undoObj.initial = true;
        return;
    }

    if (undo)
        q[1].undoChanges(q[0]);
    else
        q[1].redoChanges(q[0]);
};

var SupportedModes = {
    "application/javascript": "javascript",
    "application/json": "json",
    "text/css": "css",
    "text/x-scss": "scss",
    "text/html": "html",
    "application/xhtml+xml": "html",
    "application/xml": "xml",
    "application/rdf+xml": "xml",
    "application/rss+xml": "xml",
    "image/svg+xml": "svg",
    "application/wsdl+xml": "xml",
    "application/xslt+xml": "xml",
    "application/atom+xml": "xml",
    "application/mathml+xml": "xml",
    "application/x-httpd-php": "php",
    "text/x-script.python": "python",
    "text/x-script.ruby": "ruby",
    "text/x-script.perl": "perl",
    "text/x-script.perl-module": "perl",
    "text/x-c": "c_cpp",
    "text/x-java-source": "java",
    "text/x-groovy": "groovy",
    "text/x-csharp": "csharp",
    "text/x-script.coffeescript": "coffee",
    "text/x-markdown": "markdown",
    "text/x-web-textile": "textile",
    "text/x-script.ocaml": "ocaml",
    "text/x-script.clojure": "clojure",
    "application/x-latex": "latex",
    "text/x-lua": "lua",
    "text/x-script.powershell": "powershell",
    "text/x-scala": "scala",
    "text/x-coldfusion": "coldfusion",
    "text/x-sql": "sql"
};

var contentTypes = {
    "js": "application/javascript",
    "json": "application/json",
    "css": "text/css",
    "less": "text/css",
    "scss": "text/x-scss",
    "sass": "text/x-sass",

    "xml": "application/xml",
    "rdf": "application/rdf+xml",
    "rss": "application/rss+xml",
    "svg": "image/svg+xml",
    "wsdl": "application/wsdl+xml",
    "xslt": "application/xslt+xml",
    "atom": "application/atom+xml",
    "mathml": "application/mathml+xml",
    "mml": "application/mathml+xml",

    "php": "application/x-httpd-php",
    "phtml": "application/x-httpd-php",
    "html": "text/html",
    "xhtml": "application/xhtml+xml",
    "coffee": "text/x-script.coffeescript",
    "*Cakefile": "text/x-script.coffeescript",
    "py": "text/x-script.python",

    "ru": "text/x-script.ruby",
    "gemspec": "text/x-script.ruby",
    "rake": "text/x-script.ruby",
    "rb": "text/x-script.ruby",

    "c": "text/x-c",
    "cc": "text/x-c",
    "cpp": "text/x-c",
    "cxx": "text/x-c",
    "h": "text/x-c",
    "hh": "text/x-c",

    "cs": "text/x-csharp",

    "java": "text/x-java-source",
    "clj": "text/x-script.clojure",
    "groovy": "text/x-groovy",
    "scala": "text/x-scala",

    "ml": "text/x-script.ocaml",
    "mli": "text/x-script.ocaml",

    "md": "text/x-markdown",
    "markdown": "text/x-markdown",
    "textile": "text/x-web-textile",
    "latex": "application/x-latex",
    "tex": "application/x-latex",
    "ltx": "application/x-latex",

    "lua": "text/x-lua",

    "pl": "text/x-script.perl",
    "pm": "text/x-script.perl-module",

    "ps1": "text/x-script.powershell",
    "cfm": "text/x-coldfusion",
    "sql": "text/x-sql"
};

module.exports = ext.register("ext/code/code", {
    name    : "Code Editor",
    dev     : "Ajax.org",
    type    : ext.EDITOR,
    markup  : markup,
    deps    : [editors],

    nodes : [],

    fileExtensions : Object.keys(contentTypes),
    commandManager : new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands),

    getState : function(doc) {
        doc = doc ? doc.acesession : this.getDocument();
        if (!doc || typeof doc.getSelection != "function")
            return;

        var folds = doc.getAllFolds().map(function(fold) {
            return {
                start: fold.start,
                end: fold.end,
                placeholder: fold.placeholder
            };
        });

        var sel = doc.getSelection();
        return {
            scrolltop  : doc.getScrollTop(),
            scrollleft : doc.getScrollLeft(),
            selection  : sel.getRange(),
            folds      : folds
        };
    },

    setState : function(doc, state){
        var aceDoc = doc ? doc.acesession : this.getDocument();
        if (!aceDoc || !state || typeof aceDoc.getSelection != "function")
            return;

        var sel = aceDoc.getSelection();

        //are those 3 lines set the values in per document base or are global for editor
        sel.setSelectionRange(state.selection, false);

        aceDoc.setScrollTop(state.scrolltop);
        aceDoc.setScrollLeft(state.scrollleft);

        if (state.folds) {
            for (var i = 0, l=state.folds.length; i < l; i++) {
                var fold = state.folds[i];
                aceDoc.addFold(fold.placeholder, Range.fromPoints(fold.start, fold.end));
            }
        }

        // if newfile == 1 and there is text cached, restore it
        var node = doc.getNode && doc.getNode();
        if (node && parseInt(node.getAttribute("newfile") || 0, 10) === 1 && node.childNodes.length) {
            // the text is cached within a CDATA block as first childNode of the <file>
            if (doc.getNode().childNodes[0] instanceof CDATASection) {
                aceDoc.setValue(doc.getNode().childNodes[0].nodeValue);
            }
        }
    },

    getSyntax : function(node) {
        if (!node)
            return "";

        var mime = node.getAttribute("customtype");

        if (!mime) {
            var fileName = node.getAttribute("name");

            if (fileName.lastIndexOf(".") != -1)
                mime = contentTypes[fileName.split(".").pop()];
            else
                mime = contentTypes["*" + fileName];
        }

        if (mime) {
            mime = mime.split(";")[0];
            return (SupportedModes[mime] || "text");
        }

        return "text";
    },

    getSelection : function(){
        if (typeof ceEditor == "undefined")
            return null;
        return ceEditor.getSelection();
    },

    getDocument : function(){
        if (typeof ceEditor == "undefined")
            return null;
        return ceEditor.getSession();
    },

    setDocument : function(doc, actiontracker){
        var _self = this;

        if (!doc.acesession) {
            doc.isInited = doc.hasValue();
            doc.acedoc = doc.acedoc || new ProxyDocument(new Document(doc.getValue() || ""));
            doc.acesession = new EditSession(doc.acedoc);
            doc.acedoc = doc.acesession.getDocument();

            doc.acesession.setUndoManager(actiontracker);

            if (doc.isInited && doc.state)
                 _self.setState(doc, doc.state);

            doc.addEventListener("prop.value", function(e) {
                if (this.editor != _self)
                    return;

                doc.acesession.setValue(e.value || "");
                if (doc.state)
                    _self.setState(doc, doc.state);
                doc.isInited = true;
            });

            doc.addEventListener("retrievevalue", function(e) {
                if (this.editor != _self)
                    return;

                if (!doc.isInited)
                    return e.value;
                else
                    return doc.acesession.getValue();
            });

            doc.addEventListener("close", function(){
                if (this.editor != _self)
                    return;

                //??? destroy doc.acesession
            });
        }
        ceEditor.setProperty("value", doc.acesession);

        if (doc.editor && doc.editor != this) {
            var value = doc.getValue();
            if (doc.acesession.getValue() !== value) {
                doc.editor = this;
                doc.dispatchEvent("prop.value", {value : value});
            }
        }

        doc.editor = this;
    },

    hook: function() {
        var _self      = this;

        //Settings Support
        ide.addEventListener("init.ext/settings/settings", function(e) {
            var heading = e.ext.getHeading("Code Editor");
            heading.insertMarkup(markupSettings);
        });

        ide.addEventListener("loadsettings", function(e) {
            var model = e.model;
            if (!model.queryNode("editors/code")) {
                var node = apf.n("<code />")
                  .attr("overwrite", "false")
                  .attr("selectstyle", "line")
                  .attr("activeline", "true")
                  .attr("showinvisibles", "false")
                  .attr("showprintmargin", "true")
                  .attr("printmargincolumn", "80")
                  .attr("softtabs", "true")
                  .attr("tabsize", "4")
                  .attr("scrollspeed", "2")
                  .attr("fontsize", "12")
                  .attr("wrapmode", "false")
                  .attr("wraplimitmin", "")
                  .attr("wraplimitmax", "")
                  .attr("gutter", "true")
                  .attr("highlightselectedword", "true")
                  .attr("autohidehorscrollbar", "true").node();

                var editors = apf.createNodeFromXpath(model.data, "editors");
                apf.xmldb.appendChild(editors, node);
            }

            // pre load theme
            var theme = e.model.queryValue("editors/code/@theme");
            if (theme)
                require([theme], function() {});
            // pre load custom mime types
            _self.getCustomTypes(e.model);
        });

        ide.addEventListener("afteropenfile", function(e) {
            if (_self.setState)
                _self.setState(e.doc, e.doc.state);

            if (e.doc && e.doc.editor && e.doc.editor.ceEditor) {
                // check if there is a scriptid, if not check if the file is somewhere in the stack
                if (mdlDbgStack && mdlDbgStack.data
                    && !e.node.getAttribute("scriptid") && e.node.getAttribute("scriptname"))
                {
                    var nodes = mdlDbgStack.data.selectNodes("//frame[@script='" + e.node.getAttribute("scriptname").replace(ide.workspaceDir + "/", "") + "']");
                    if (nodes.length) {
                        e.node.setAttribute("scriptid", nodes[0].getAttribute("scriptid"));
                    }
                }
                e.doc.editor.ceEditor.afterOpenFile(e.doc.editor.ceEditor.getSession());
            }
        });

        tabEditors.addEventListener("afterswitch", function(e) {
            ceEditor.afterOpenFile(ceEditor.getSession());
        });

        // preload common language modes
        require(["ace/mode/javascript", "ace/mode/html", "ace/mode/css"], function() {});
    },

    init: function(amlPage) {
        amlPage.appendChild(ceEditor);
        ceEditor.show();

        this.ceEditor = this.amlEditor = ceEditor;
        ceEditor.$editor.commands = this.commandManager;

        var _self = this;

        this.nodes.push(
            //Add a panel to the statusbar showing whether the insert button is pressed
            sbMain.appendChild(new apf.section({
                caption : "{ceEditor.insert}"
            })),

            //Add a panel to the statusbar showing the length of the document
            sbMain.appendChild(new apf.section({
                caption : "Length: {ceEditor.value.length}"
            })),

            mnuView.appendChild(new apf.item({
                caption : "Syntax Highlighting",
                submenu : "mnuSyntax"
            })),

            mnuView.appendChild(new apf.divider()),

            mnuView.appendChild(new apf.item({
                type    : "check",
                caption : "Show Invisibles",
                checked : "[{require('ext/settings/settings').model}::editors/code/@showinvisibles]"
            })),

            mnuView.appendChild(new apf.item({
                type    : "check",
                caption : "Wrap Lines",
                checked : "{ceEditor.wrapmode}"
            }))
        );

        mnuSyntax.onitemclick = function(e) {
            var file = ide.getActivePageModel();

            if (file) {
                var value = e.relatedNode.value;

                if (value == "auto")
                    apf.xmldb.removeAttribute(file, "customtype", "");
                else
                    apf.xmldb.setAttribute(file, "customtype", value);

                if (file.getAttribute("customtype")) {
                    var fileName = file.getAttribute("name");

                    if (contentTypes["*" + fileName])
                        delete contentTypes["*" + fileName];

                    var mime = value.split(";")[0];
                    var fileExt = (fileName.lastIndexOf(".") != -1) ?
                        fileName.split(".").pop() : null;

                    if (fileExt && contentTypes[fileExt] !== mime)
                        delete contentTypes[fileExt];

                    var customType = fileExt ?
                        contentTypes[fileExt] : contentTypes["*" + fileName];

                    if (!customType)
                        _self.setCustomType(fileExt ? fileExt : file, mime);
                    ide.dispatchEvent("track_action", {
                        type: "syntax highlighting",
                        fileType: fileExt,
                        fileName: fileName,
                        mime: mime,
                        customType: customType
                    });
                }
            }
        };

        ide.addEventListener("keybindingschange", function(e) {
            if (typeof ceEditor == "undefined")
                return;

            var bindings = e.keybindings.code;
            ceEditor.$editor.setKeyboardHandler(new HashHandler(bindings));
            // In case the `keybindingschange` event gets fired after other
            // plugins that change keybindings have already changed them (i.e.
            // the vim plugin), we fire an event so these plugins can react to it.
            ide.dispatchEvent("code.ext:defaultbindingsrestored", {
                bindings: ceEditor.$editor.getKeyboardHandler()
            });
        });
    },

    /**
     * Saves custom syntax for extension type in settings.xml
     *
     * @param {String|xmlNode} ext Contains the extension type shorthand
     * @param {String} mime Mime type string the extension will be related to
     */
    setCustomType: function(ext, mime) {
        var node;

        if (typeof ext === "string") {
            node = settings.model.queryNode('auto/customtypes/mime[@ext="' + ext + '"]');
            if (!node)
                settings.model.appendXml('<mime name="' + mime + '" ext="' + ext + '" />', "auto/customtypes");
        } else {
            var name = ext.getAttribute("name") || "";
            node = settings.model.queryNode('auto/customtypes/mime[@filename="' + name + '"]');
            if (node)
                apf.xmldb.removeAttribute(node, "ext");
            else
                settings.model.appendXml('<mime name="' + mime + '" filename="' + name + '" />', "auto/customtypes");
        }

        apf.xmldb.setAttribute(node, "name", mime);
        settings.save();
    },

    /**
     * Retrieves custom syntax for extensions saved in settings.xml
     *
     * @param {Object} model Settings' model
     */
    getCustomTypes: function(model) {
        var customTypes = model.queryNode("auto/customtypes");
        if (!customTypes)
            customTypes = apf.createNodeFromXpath(model.data, "auto/customtypes");

        var mimes = customTypes.selectNodes("mime");
        mimes.forEach(function(n) {
            if (n.getAttribute("filename"))
                contentTypes["*" + n.getAttribute("filename")] = n.getAttribute("name");
            else
                contentTypes[n.getAttribute("ext")] = n.getAttribute("name");
        });
    },


    enable : function() {
        this.nodes.each(function(item){
            item.show();
        });
    },

    disable : function() {
        this.nodes.each(function(item){
            item.hide();
        });
    },

    destroy : function(){
        this.nodes.each(function(item){
            item.destroy(true, true);
        });

        if (self.ceEditor) {
            ceEditor.destroy(true, true);
            mnuSyntax.destroy(true, true);
        }

        this.nodes = [];
    }
});

});
Example #29
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var util = require("core/util");
var panels = require("ext/panels/panels");
var dockpanel = require("ext/dockpanel/dockpanel");
var settings = require("ext/settings/settings");

module.exports = ext.register("ext/editors/editors", {
    name    : "Editors",
    dev     : "Ajax.org",
    alone   : true,
    type    : ext.GENERAL,
    nodes   : [],
    visible : true,
    alwayson : true,

    contentTypes  : {},

    register : function(oExtension){
        var id = "rb" + oExtension.path.replace(/\//g, "_");

        /*oExtension.$rbEditor = barButtons.appendChild(new apf.radiobutton({
            id        : id,
            label     : oExtension.name,
            value     : oExtension.path,
            margin    : "0 -1 0 0",
            visible   : "{require('ext/editors/editors').isEditorAvailable(tabEditors.activepage, '" + oExtension.path + "')}",
            onclick   : function(){
                require('ext/editors/editors').switchEditor(this.value);
            }
        }));*/

        //Add a menu item to the list of editors
        /*oExtension.$itmEditor = ide.mnuEditors.appendChild(new apf.item({
            type    : "radio",
            caption : oExtension.name,
            value   : oExtension.path,
            onclick : function(){
                require('ext/editors/editors').switchEditor(this.value);
            }
        }));*/

        var _self = this;
        oExtension.contentTypes.each(function(mime){
            (_self.contentTypes[mime] || (_self.contentTypes[mime] = [])).push(oExtension);
        });

        if (!this.contentTypes["default"] || (oExtension.name && oExtension.name == "Code Editor"))
            this.contentTypes["default"] = oExtension;
    },

    unregister : function(oExtension){
        //oExtension.$rbEditor.destroy(true, true);
        //oExtension.$itmEditor.destroy(true, true);

        var _self = this;
        oExtension.contentTypes.each(function(fe){
            _self.contentTypes[fe].remove(oExtension);
            if (!_self.contentTypes[fe].length)
                delete _self.contentTypes[fe];
        });

        if (this.contentTypes["default"] == oExtension) {
            delete this.contentTypes["default"];

            for (var prop in this.contentTypes) {
                this.contentTypes["default"] = this.contentTypes[prop][0];
                break;
            }
        }
    },

    addTabSection : function(){
        var _self = this;
        var vbox = this.hbox.appendChild(
            new apf.bar({id:"tabPlaceholder", flex:1, skin:"basic"})
        );

        var btn;
        var tab = new apf.bar({
            skin     : "basic",
            style    : "padding : 0 0 33px 0;position:absolute;", //53px
            htmlNode : document.body,
            childNodes: [
                new apf.tab({
                    id      : "tabEditors",
                    skin    : "editor_tab",
                    style   : "height : 100%",
                    buttons : "close,scale,order",
                    overactivetab  : true,
                    onfocus        : function(e){
                        _self.switchfocus(e);
                    },
                    onbeforeswitch : function(e){
                        _self.beforeswitch(e);
                    },
                    onafterswitch : function(e){
                        _self.afterswitch(e);
                    },
                    onclose : function(e){
                        if (!ide.onLine && !ide.offlineFileSystemSupport) //For now prevent tabs from being closed
                            return false;
                            
                        _self.close(e.page);
                    },
                    childNodes : [
                        btn = new apf.button({
                            style : "display:inline-block;margin: 0 0 5px 13px;",
                            right : 5,
                            top   : 8,
                            width : 30,
                            height : 17,
                            skin : "btn_icon_only",
                            background : "plustabbtn.png|horizontal|3|30",
                            onclick : function(){
                                require("ext/newresource/newresource").newfile();
                            }
                        })
                    ]
                }),
                new apf.button({
                    top   : 8,
                    left  : 5,
                    width : 17,
                    height : 17,
                    submenu : "mnuTabs",
                    skin : "btn_icon_only",
                    "class" : "tabmenubtn",
                    background : "tabdropdown.png|horizontal|3|17"
                }) /*,
                new apf.hbox({
                    id      : "barButtons",
                    edge    : "0 0 0 6",
                    "class" : "relative",
                    zindex  : "1000",
                    bottom  : "0",
                    left    : "0",
                    right   : "0"
                })*/
            ]
        });
        
        tabEditors.$buttons.appendChild(btn.$ext);
        tabEditors.addEventListener("DOMNodeInserted",function(e){
            if (e.$isMoveWithinParent) {
                //record position in settings
                
                var amlNode = e.currentTarget;
                if (amlNode.localName != "page" || e.relatedNode != this || amlNode.nodeType != 1)
                    return;
                
                settings.save();
            }
            
            if (e.relatedNode == this && e.currentTarget.localName == "page") {
                tabEditors.$buttons.appendChild(btn.$ext);
                btn.$ext.style.position = "";
                btn.$ext.style.right = "";
                btn.$ext.style.top = "";
            }
        });
        
        tabEditors.addEventListener("DOMNodeRemoved",function(e){
            if (e.relatedNode == this && this.getPages().length == 1) {
                btn.$ext.style.position = "absolute";
                btn.$ext.style.right = "5px";
                btn.$ext.style.top = "8px";
            }
        });
        
        tabPlaceholder.addEventListener("resize", this.$tabPlaceholderResize = function(e){
            _self.setTabResizeValues(tab.$ext);
        });

        return vbox;
    },
    
    /**
     * This method has been abstracted so it can be used by
     * the focus extension to get the destination coordinates and
     * dimensions of tabEditors.parentNode when the editor goes
     * out of focus mode
     */
    setTabResizeValues : function(ext) {
        var ph;
        var pos = apf.getAbsolutePosition(ph = tabPlaceholder.$ext);
        ext.style.left = (pos[0] - 2) + "px";
        ext.style.top = pos[1] + "px";
        var d = apf.getDiff(ext);
        ext.style.width = (ph.offsetWidth + 2 + (apf.isGecko && dockpanel.visible ? 2 : 0) - d[0]) + "px";
        ext.style.height = (ph.offsetHeight - d[1]) + "px";
    },

    /**
     * Disable the resize event when the editors are in focus mode
     */
    disableTabResizeEvent : function() {
        tabPlaceholder.removeEventListener("resize", this.$tabPlaceholderResize);
    },

    /**
     * Enable the resize event when the editors come back to non-focus mode
     */
    enableTabResizeEvent : function() {
        tabPlaceholder.addEventListener("resize", this.$tabPlaceholderResize);
    },

    isEditorAvailable : function(page, path){
        var editor = ext.extLut[path];
        if (!editor)
            return false;

        var contentTypes = editor.contentTypes;
        var isEnabled = contentTypes.indexOf(tabEditors.getPage(page).contentType) > -1;
        
        if (!isEnabled && this.contentTypes["default"] == editor)
            return true; 
        else
            return isEnabled;
    },

    initEditor : function(editor){
        //Create Page Element
        var editorPage = new apf.page({
            id        : editor.path,
            mimeTypes : editor.contentTypes,
            visible   : false,
            realtime  : false
        });
        tabEditors.appendChild(editorPage);

        //Initialize Content of the page
        ext.initExtension(editor, editorPage);

        return editorPage;
    },

    switchEditor : function(path){
        var page = tabEditors.getPage();
        if (!page || page.type == path)
            return;

        var lastType = page.type;

        var editor = ext.extLut[path];
        if (!editor.inited)
            this.initEditor(editor);

        //editor.$itmEditor.select();
        //editor.$rbEditor.select();

        page.setAttribute("type", path);

        this.beforeswitch({nextPage: page});
        this.afterswitch({nextPage: page, previousPage: {type: lastType}});
    },

    openEditor : function(doc, init, active) {
        var xmlNode  = doc.getNode();
        var filename = xmlNode.getAttribute("name");
        var filepath = xmlNode.getAttribute("path");

        var page = tabEditors.getPage(filepath);
        if (page) {
            tabEditors.set(page);
            return;
        }

        var contentType = (xmlNode.getAttribute("contenttype") || "").split(";")[0];
        var editor = this.contentTypes[contentType] && this.contentTypes[contentType][0] || this.contentTypes["default"];

        if (!init && this.currentEditor)
            this.currentEditor.disable();

        if (!editor) {
            util.alert(
                "No editor is registered",
                "Could not find an editor to display content",
                "There is something wrong with the configuration of your IDE. No editor plugin is found.");
            return;
        }

        if (!editor.inited)
            this.initEditor(editor);
        
        //Create Fake Page
        if (init)
            tabEditors.setAttribute("buttons", "close");
        
        var model = new apf.model();
        var fake = tabEditors.add("{([@changed] == 1 ? '*' : '') + [@name]}", filepath, editor.path, null, function(page){
            page.contentType = contentType;
            page.$at     = new apf.actiontracker();
            page.$doc    = doc;
            doc.$page    = page;
            page.$editor = editor;
            page.setAttribute("tooltip", "[@path]");
            page.setAttribute("class",
                "{parseInt([@saving], 10) || parseInt([@lookup], 10) ? (tabEditors.getPage(tabEditors.activepage) == this ? 'saving_active' : 'saving') : \
                ([@loading] ? (tabEditors.getPage(tabEditors.activepage) == this ? 'loading_active' : 'loading') : '')}"
            );
            page.setAttribute("model", page.$model = model);
            page.$model.load(xmlNode);
        });

        if (init)
            tabEditors.setAttribute("buttons", "close,scale,order");
        
        var editorPage = tabEditors.getPage(tabEditors.activepage);
        
        doc.addEventListener("setnode", function(e) {
            fake.$model.load(e.node);
            ide.dispatchEvent("afteropenfile", {doc: doc, node: e.node, editor: editor});
        });
        
        this.initEditorEvents(fake, model);

        if (init && !active)
            return;
        
        //Set active page
        tabEditors.set(filepath);

        //if (editorPage.model != model)
            //this.beforeswitch({nextPage: fake});

        //Open tab, set as active and wait until opened
        /*fake.addEventListener("afteropen", function(){

        });*/
        
        editor.enable();
        //editor.$itmEditor.select();
        //editor.$rbEditor.select();

        this.currentEditor = editor;
        
        // okay don't know if you would want this, but this is the way the 'open file' dialog
        // handles it so let's do that
        setTimeout(function () {
            ceEditor.focus();
        }, 100);
        
        settings.save();
    },
    
    initEditorEvents: function(fake, model) {
        fake.$at.addEventListener("afterchange", function(e) {
            if (e.action == "reset") {
                delete this.undo_ptr;
                return;
            }
            
            var val;
            if (fake.$at.ignoreChange) {
                val = undefined;
                fake.$at.ignoreChange = false;
            }
            else if(this.undolength === 0 && !this.undo_ptr) {
                val = undefined;
            }
            else {
                val = (this.$undostack[this.$undostack.length - 1] !== this.undo_ptr) 
                    ? 1 
                    : undefined;
            }

            if (fake.changed !== val) {
                fake.changed = val;
                model.setQueryValue("@changed", (val ? "1" : "0"));
            }
        });
    },

    close : function(page) {
        page.addEventListener("afterclose", this.$close);
    },

    $close : function() {
        var page = this;
        var at   = page.$at;
        var mdl  = page.$model;
        
        mdl.setQueryValue("@changed", 0);
        page.$doc.dispatchEvent("close");
        
        if (mdl.data) {
            mdl.removeXml("data");
            ide.dispatchEvent("closefile", {xmlNode: mdl.data, page: page});
        }
        
        //mdl.unshare();
        mdl.destroy();

        at.reset();
        at.destroy();
        
        //If there are no more pages left, reset location
        if (!tabEditors.getPage()) {
            /*if (window.history.pushState) {
                var p = location.pathname.split("/");
                window.history.pushState(path, path, "/" + (p[1] || "") + "/" + (p[2] || ""));
            }
            else {
                apf.history.setHash("");
            }*/
            apf.history.setHash("");
        }
        
        //Destroy the app page if it has no application instance
        //if (!tabEditors.selectNodes("page[@type='" + page.type + "']").length && editorPage)
            //editorPage.destroy(true, true);
        
        settings.save();
    },

    switchfocus : function(e){

    },

    beforeswitch : function(e) {
        var page       = e.nextPage,
            editorPage = tabEditors.getPage(page.type);
        if (!editorPage) return;

        if (editorPage.model != page.$model)
            editorPage.setAttribute("model", page.$model);
        if (editorPage.actiontracker != page.$at)
            editorPage.setAttribute("actiontracker", page.$at);
        
        page.$editor.setDocument && page.$editor.setDocument(page.$doc, page.$at);
        
        ide.dispatchEvent("editorswitch", {
            previousPage: e.previousPage,
            nextPage: e.nextPage
        });
    },

    afterswitch : function(e) {
        var page = e.nextPage;
        var fromHandler, toHandler = ext.extLut[page.type];

        if (e.previousPage && e.previousPage != e.nextPage)
            fromHandler = ext.extLut[e.previousPage.type];

        if (fromHandler != toHandler) {
            if (fromHandler)
                fromHandler.disable();
            toHandler.enable();
        }
        
        var path = page.$model.data.getAttribute("path").replace(/^\/workspace/, "");
        /*if (window.history.pushState) {
            var p = location.pathname.split("/");
            window.history.pushState(path, path, "/" + (p[1] || "name") + "/" + (p[2] || "project") + path);
        }
        else {
            apf.history.setHash("!" + path);
        }*/
        apf.history.setHash("!" + path);
        
        //toHandler.$itmEditor.select();
        //toHandler.$rbEditor.select();

        /*if (self.TESTING) {}
            //do nothing
        else if (page.appid)
            app.navigateTo(page.appid + "/" + page.id);
        else if (!page.id)
            app.navigateTo(app.loc || (app.loc = "myhome"));*/
    },

    /**** Init ****/

    hook : function(){
        panels.register(this);
        
        window.onpopstate = function(e){
            var page = "/workspace" + e.state;
            if (tabEditors.activepage != page && tabEditors.getPage(page))
                tabEditors.set(page);
        };

        apf.addEventListener("hashchange", function(e){
            var page = "/workspace" + e.page;
            if (tabEditors.activepage != page && tabEditors.getPage(page))
                tabEditors.set(page);
        });
    },

    init : function(){
        var _self = this;
        ext.addType("Editor", function(oExtension){
            _self.register(oExtension);
          }, function(oExtension){
            _self.unregister(oExtension);
          });

        ide.addEventListener("openfile", function(e){
            _self.openEditor(e.doc, e.init, e.active);
        });

        ide.addEventListener("filenotfound", function(e) {
            var page = tabEditors.getPage(e.path);
            if (page)
                tabEditors.remove(page);
        });

        var vbox  = colMiddle;
        this.hbox = vbox.appendChild(new apf.hbox({flex : 1, padding : 5, splitters : true}));
        //this.splitter = vbox.appendChild(new apf.splitter());
        this.nodes.push(this.addTabSection());

        this.panel = this.hbox;

        /**** Support for state preservation ****/

        this.$settings = {};
        ide.addEventListener("loadsettings", function(e){
            function checkExpand(path, doc) {
                var parent_path = apf.getDirname(path).replace(/\/$/, "");
                var expandEventListener = function(e) {
                    if (e.xmlNode && e.xmlNode.getAttribute("path") == parent_path) {
                        if (doc.getNode().getAttribute("newfile") != 1)
                            doc.setNode(e.xmlNode.selectSingleNode("node()[@path='" + path + "']"));
                        trFiles.removeEventListener("expand", expandEventListener);
                    }
                };
                
                trFiles.addEventListener("expand", expandEventListener);
            }
            
            var model = e.model;
            ide.addEventListener("extload", function(){            
                
                // you can load a file from the hash tag, if that succeeded then return
                var loadFileFromHash =  (_self.loadFileFromHash(window.location.hash, checkExpand));
                if (loadFileFromHash) {
                    window.location.hash = loadFileFromHash; // update hash
                    return;
                }
                
                // otherwise, restore state from the .config file
                var active = model.queryValue("auto/files/@active");
                var nodes  = model.queryNodes("auto/files/file");
                
                for (var doc, i = 0, l = nodes.length; i < l; i++) {
                    doc = ide.createDocument(nodes[i]);
                    
                    var state = nodes[i].getAttribute("state");
                    try {
                        if (state)
                            doc.state = JSON.parse(state);
                    }
                    catch (ex) {}
                    
                    if (nodes[i].getAttribute("changed") == 1) {
                        doc.cachedValue = nodes[i].firstChild.nodeValue
                            .replace(/\n]\n]/g, "]]")
                            .replace(/\\r/g, "\r")
                            .replace(/\\n/g, "\n");
                    }
                    
                    ide.dispatchEvent("openfile", {
                        doc    : doc,
                        init   : true,
                        active : active 
                            ? active == nodes[i].getAttribute("path")
                            : i == l - 1
                    });
                    
                    checkExpand(nodes[i].getAttribute("path"), doc);
                }
            });
        });

        ide.addEventListener("savesettings", function(e){
            var changed = false,
                pNode   = e.model.data.selectSingleNode("auto/files"),
                state   = pNode && pNode.xml,
                pages   = tabEditors.getPages();

            if (pNode) {
                pNode.parentNode.removeChild(pNode);
                pNode = null;
            }

            if (pages.length) {
                var active = tabEditors.activepage;
                e.model.setQueryValue("auto/files/@active", active);
                
                pNode = apf.createNodeFromXpath(e.model.data, "auto/files");
                for (var i = 0, l = pages.length; i < l; i++) {
                    var file = pages[i].$model.data;
                    if (!file || file.getAttribute("debug"))
                        continue;

                    var copy = apf.xmldb.cleanNode(file.cloneNode(false));
                    //copy.removeAttribute("changed");
                    copy.removeAttribute("loading");
                    copy.removeAttribute("saving");
                    pNode.appendChild(copy);
                    
                    var state = pages[i].$editor.getState && pages[i].$editor.getState(pages[i].$doc);
                    if (state)
                        copy.setAttribute("state", apf.serialize(state));
                    
                    //@todo the second part of this if can be removed as soon
                    //as the collab team implements stored changed settings
                    //please note that for this to work on loadsettings we 
                    //should check whether the file on disk has changed and 
                    //popup a file watch dialog to ask if the user wants to
                    //load the new file from disk, losing changes.
                    if (copy.getAttribute("changed") == 1 && copy.getAttribute("newfile") == 1) {
                        copy.appendChild(copy.ownerDocument.createCDATASection(
                            pages[i].$doc.getValue()
                                .replace(/\r/g, "\\r")
                                .replace(/\n/g, "\\n")
                                .replace(/\]\]/g, "\n]\n]")
                        ));
                    }
                }
            }

            if (state != (pNode && pNode.xml))
                return true;
        });
        
        ide.addEventListener("reload", function(e) {
            var doc = e.doc;
            doc.state = doc.$page.$editor.getState && doc.$page.$editor.getState(doc);
        });
        
        ide.addEventListener("afterreload", function(e) {
            var doc         = e.doc,
                acesession  = doc.acesession,
                sel         = acesession.getSelection();
            
            sel.selectAll();
            acesession.getUndoManager().ignoreChange = true;
            acesession.replace(sel.getRange(), e.data);
            sel.clearSelection();
            
            if (doc.state) {
                var editor = doc.$page.$editor;
                editor.setState && editor.setState(doc, doc.state);
            }
        });
    },
    
    /** Load any file from the hash, with optional some lines selected 
     * 
     * @param {string} hash Hash as obtained from the window element
     * @param {function} checkExpand Function that expands the tree for the given file
     * @return {string} The new hash
     */
    loadFileFromHash : function (hash, checkExpand) {
        // an initial state can be sent in the hash
        // match 'openfile-', 
        // match any character except :& or end of file
        // optional: match : digit - digit
        // [1] is filename, [2] is starting line number, [3] is ending line number
        var editorInitialStatePattern = /openfile-(.[^:&$]*)(?:\:(\d+)-(\d+))?/;
        var rawState = hash.match(editorInitialStatePattern);
        
        if (rawState) {
            // build the real path, as the one in the hash is relative
            var path = ide.davPrefix.replace(/\/$/, "") + "/" + rawState[1];
            var doc = ide.createDocument(this.createFileNodeFromPath(path));

            // if selection information was added, add that to the state
            if (rawState[2] && rawState[3]) {
                doc.state = {
                    scrollleft: 0, scrolltop: 0,
                    selection: {
                        start: { row: parseInt(rawState[2] || 0, 10) - 1, column: 0 },
                        end: { row: parseInt(rawState[3] || 0, 10), column: 0 } // plus 1 to capture whole previous line
                    }
                };
            }
            
            // send it to the dispatcher
            ide.dispatchEvent("openfile", {
                doc: doc,
                active: true
            });
            // and expand the tree
            checkExpand(path, doc);
            
            // return the new hash
            return hash.replace(editorInitialStatePattern, "");
        }
        
        return null;
    },
    
    createFileNodeFromPath : function (path) {
        var name = path.split("/").pop();
        var node = apf.n("<file />")
            .attr("name", name)
            .attr("contenttype", util.getContentType(name))
            .attr("path", path)
            .node();
        return node;
    },

    showFile : function(path, row, column, text, state) {
        var node = this.createFileNodeFromPath(path);

        this.jump(node, row, column, text);
    },

    jump : function(fileEl, row, column, text, doc, page) {
        var path    = fileEl.getAttribute("path");
        var hasData = page && tabEditors.getPage(path).$doc ? true : false;

        if (row !== undefined) {
            var jumpTo = function(){
                setTimeout(function() {
                    // TODO move this to the editor
                    ceEditor.$editor.gotoLine(row, column);
                    if (text)
                        ceEditor.$editor.find(text);
                    ceEditor.focus();
                }, 100);
            };

            if (hasData) {
                tabEditors.set(path);
                jumpTo();
            }
            else
                ide.addEventListener("afteropenfile", function(e) {
                    var node = e.doc.getNode();
                    
                    if (node.getAttribute("path") == path) {
                        ide.removeEventListener("afteropenfile", arguments.callee);
                        jumpTo();
                    }
                });
        }
        
        if (!hasData && !page) 
            ide.dispatchEvent("openfile", {
                doc: doc || ide.createDocument(fileEl)
            });
        else
            tabEditors.set(path);
    },

    enable : function(){
        this.hbox.show();
        //this.splitter.show();
    },

    disable : function(){
        this.hbox.hide();
        //this.splitter.hide();
    },

    destroy : function(){
        this.hbox.destroy(true, true);
        //this.splitter.destroy(true, true);
        panels.unregister(this);
    }
});

});
Example #30
0
define(function(require, exports, module) {

var ide = require("core/ide");
var ext = require("core/ext");
var settings = require("core/settings");
var markup = require("text!ext/noderunner/noderunner.xml");
var c9console = require("ext/console/console");
var _debugger = require("ext/debugger/debugger");

/*global stProcessRunning*/

module.exports = ext.register("ext/noderunner/noderunner", {
    name    : "Node Runner",
    dev     : "Ajax.org",
    type    : ext.GENERAL,
    alone   : true,
    offline : false,
    autodisable : ext.ONLINE | ext.LOCAL,
    markup  : markup,

    NODE_VERSION: "auto",

    init : function() {
        var _self = this;
        if (ide.connected) {
            this.queryServerState();
            ide.addEventListener("socketDisconnect", function() {
                ide.dispatchEvent("dbg.exit");
            });
        } else {
            ide.addEventListener("socketConnect", function() {
                _self.queryServerState();
            });
        }

        ide.addEventListener("socketMessage", this.onMessage.bind(this));

        ide.addEventListener("consolecommand.run", function(e) {
            ide.send({
                command: "internal-isfile",
                argv: e.data.argv,
                cwd: e.data.cwd,
                sender: "noderunner"
            });
            return false;
        });

        this.nodePid = null;

        ide.addEventListener("settings.load", function(e){
            _self.NODE_VERSION = e.model.queryValue("auto/node-version/@version") || "auto";
        });
    },

    onMessage : function(e) {
        var message = e.message;
        //if (message.type != "shell-data")
           // console.log("MSG", message)
        var runners = window.cloud9config.runners;
        var lang;
        if ((lang = /^(\w+)-debug-ready$/.exec(message.type)) && runners.indexOf(lang[1]) >= 0) {
            ide.dispatchEvent("dbg.ready", message);
            return;
        }
        else if ((lang = /^(\w+)-exit$/.exec(message.type)) && runners.indexOf(lang[1]) >= 0) {
            ide.dispatchEvent("dbg.exit", message);
            if (message.pid == this.nodePid) {
                stProcessRunning.deactivate();
                this.nodePid = 0;
            }
            return;
        }

        switch(message.type) {
            case "state":
                this.nodePid = message.processRunning || 0;
                stProcessRunning.setProperty("active", !!message.processRunning);

                ide.dispatchEvent("dbg.state", message);
                break;

            case "error":
                // child process already running
                if (message.code == 1) {
                    stProcessRunning.setProperty("active", true);
                }
                // debug process already running
                else if (message.code == 5) {
                    stProcessRunning.setProperty("active", true);
                }
                /*
                    6:
                    401: Authorization Required
                */
                // Command error
                else if (message.code === 9) {
                    c9console.log("<div class='item console_log' style='font-weight:bold;color:yellow'>"
                        + apf.escapeXML(message.message) + "</div>");
                }
                else if (message.code !== 6 && message.code != 401 && message.code != 455 && message.code != 456) {
                    c9console.log("<div class='item console_log' style='font-weight:bold;color:#ff0000'>[C9 Server Exception "
                        + apf.escapeXML(message.code || "") + "] " + apf.escapeXML(message.message) + "</div>");

                    apf.ajax("/api/debug", {
                        method      : "POST",
                        contentType : "application/json",
                        data        : JSON.stringify({
                            agent   : navigator.userAgent,
                            type    : "C9 SERVER EXCEPTION",
                            code    : e.code,
                            message : e.message
                        })
                    });
                }

                this.queryServerState();
                break;
        }
    },

    queryServerState : function() {
        // load the state, which is quite a weird name actually, but it contains
        // info about the debugger. The response is handled by 'noderunner.js'
        // who publishes info for the UI of the debugging controls based on this.
        ide.send({command: "state", action: "publish" });
    },

    debug : function() {
    },

    run : function(path, args, debug, nodeVersion) {
        var runner;
        if (stProcessRunning.active || typeof path != "string")
            return false;
        // TODO there should be a way to set state to waiting
        stProcessRunning.activate();

        path = path.trim();

        if (nodeVersion == 'default' || !nodeVersion) {
            runner = this.detectRunner(path);
            nodeVersion = runner == 'node' ? settings.model.queryValue("auto/node-version/@version") || this.NODE_VERSION : 'auto';
        }
        else {
            runner = nodeVersion.split(" ")[0];
            nodeVersion = nodeVersion.split(" ")[1] || 'auto';
        }

        var page = ide.getActivePageModel();
        var command = {
            "command" : apf.isTrue(debug) ? "RunDebugBrk" : "Run",
            "file"    : path.replace(/^\/+/, ""),
            "runner"  : runner,
            "args"    : args || [],
            "version" : nodeVersion,
            "env"     : {
                "C9_SELECTED_FILE": page ? page.getAttribute("path").slice(ide.davPrefix.length) : ""
            }
        };
        ide.send(command);
    },

    stop : function() {
        if (!stProcessRunning.active)
            return;

        ide.send({
            "command": "kill",
            "runner" : "node",
            "pid"    : this.nodePid
        });
        this.queryServerState();
    },

    enable : function(){
    },

    disable : function(){
    },

    destroy : function(){
    },

    detectRunner: function(path) {
        if (path.match(/\.(php|phtml)$/))
            return "apache";

        if (path.match(/\.py$/))
            return "python";

        if (path.match(/\.rb$/))
            return "ruby";

        return "node";
    }
});

});