<commit_before>

export default class AceEditor extends HTMLElement {

    // Fires when an instance was inserted into the document
    attachedCallback() {
        var text = this.childNodes[0];
        var container = this.container;
        var element = this;

        if(this.editor){
            var editor = this.editor;
            this.value = text.textContent || this.value;
        } else {
            // container.appendChild(text);
            container.innerHTML = this.innerHTML || this.value;
            editor = ace.edit(container);
            this.dispatchEvent(new CustomEvent("editor-ready", {detail: editor}));
            this.editor = editor;

            // inject base editor styles
            this.injectTheme('#ace_editor\\.css');
            this.injectTheme('#ace-tm');

            editor.getSession().on('change', function(event){
                element.dispatchEvent(new CustomEvent("change", {detail: event}));
            });
        }

        // handle theme changes
        editor.renderer.addEventListener("themeLoaded", this.onThemeLoaded.bind(this));

        // initial attributes
            editor.setTheme( this.getAttribute("theme") );
            editor.setFontSize( this.getAttribute("fontsize") );
            editor.setReadOnly( this.getAttribute("readonly") );
            var session = editor.getSession();
            session.setMode( this.getAttribute("mode") );
            session.setUseSoftTabs( this.getAttribute("softtabs") );
            this.getAttribute("tabsize") && session.setTabSize( this.getAttribute("tabsize") );
            session.setUseWrapMode( this.hasAttribute("wrapmode") );

        // prevent error message when CTRL+P
        editor.$blockScrolling = Infinity

        // Observe input textNode changes
        // Could be buggy as editor was also added to Light DOM;
            var observer = new MutationObserver(function(mutations) {
              mutations.forEach(function(mutation) {
                // console.log("observation", mutation.type, arguments, mutations, editor, text);
                if(mutation.type == "characterData"){
                    element.value = text.data;
                }
              });
            });
            text && observer.observe(text, { characterData: true });
        // container.appendChild(text);
        this._attached = true;

        this.customizeEditor();

        this.loadSpellcheck()
        this.enableSpellcheck()

        // editor.resize()
        window.setTimeout(() => {
          this.editor.resize();
        }, 50) // #Hack #Resize #RaceCondition

        var _this = this;
        window.setTimeout(() => {
          // we also use this timeout hack, but we should get rid of it!!
          new ResizeSensor($(_this.container), () => {
            this.editor.resize();
          });
        }, 500);
    };

    // Fires when an instance was removed from the document
    detachedCallback() {
        this._attached = false;
    };

    // Fires when an attribute was added, removed, or updated
    attributeChangedCallback(attr, oldVal, newVal) {
        if(!this._attached){
            return false;
        }
        switch(attr){
            case "theme":
                this.editor.setTheme( newVal );
                break;
            case "mode":
                this.editor.getSession().setMode( newVal );
                break;
            case "fontsize":
                this.editor.setFontSize( newVal );
                break;
            case "softtabs":
                this.editor.getSession().setUseSoftTabs( newVal );
                break;
            case "tabsize":
                this.editor.getSession().setTabSize( newVal );
                break;
            case "readonly":
                this.editor.setReadOnly( newVal );
                break;
            case "wrapmode":
                this.editor.getSession().setUseWrapMode( newVal !== null );
                break;

        }
    };

    onThemeLoaded(e){
        var themeId = "#" + e.theme.cssClass;
        this.injectTheme(themeId);
        // Workaround Chrome stable bug, force repaint
        this.container.style.display='none';
        this.container.offsetHeight;
        this.container.style.display='';
    };

    // inject the style tag of a theme to the element
    injectTheme(themeId){

        //helper function to clone a style
        function cloneStyle(style) {
            var s = document.createElement('style');
            s.id = style.id;
            s.textContent = style.textContent;
            return s;
        }

        var n = document.querySelector(themeId);
        this.shadowRoot.appendChild(cloneStyle(n));
    };

    // CUSTOMIZATION
    enableAutocompletion(filename) {
      return this.aceRequire("ace/ext/language_tools").then( module => {
        this.editor.setOptions({
            enableBasicAutocompletion: true,
            enableSnippets: true,
            enableLiveAutocompletion: false
        });
        this.editor.completers[3] =  {
          getCompletions: function(editor, session, pos, prefix, callback) {
              // console.log("getCompletions: " + pos + " prefix " + prefix)
              var curLine = session.getDocument().getLine(pos.row);
              var curTokens = curLine.slice(0, pos.column).split(/\s+/);
              var curCmd = _.last(curTokens);
              // console.log("line : " + curLine + " curTokens: " + curTokens + " curCmd:" + curCmd)
              if (!curCmd) return;
              try {
                var wordList = [];
              curCmd = curCmd.replace(/\.[^.]*$/,"")
              var obj = eval(curCmd);
              wordList = lively.allProperties(obj);
              // console.log("complete: " + curCmd +"\n" + wordList)
                callback(null, _.keys(wordList).map(function(ea) {
                  return {name: ea, value: ea, score: 300, meta: wordList[ea]};
                  }));
              } catch(err) {
                console.log("Could not complete: " + curCmd +", because of:" + err)
              }
            }
          }
      })
    }
    changeModeForFile(filename) {
      var modelist = ace.require("ace/ext/modelist");
      var mode = modelist.getModeForPath(filename).name;
      console.log(filename + " -> " + mode);

      this.changeMode(mode);

    }

    changeMode(mode) {
      var Mode;
      try {
        Mode = ace.require("ace/mode/" + mode).Mode;
        this.editor.session.setMode(new Mode());
      } catch (e) {
        console.log("ace-editor: lazy load ace mode " + mode)
        // mode is not loaded, so try to load it,
        // there must be a way to make ace do all this stuff as with themes... :(
        var script = document.createElement("Script");
        script.type = "text/javascript";
        script.src = lively4url + "/src/external/ace/mode-" + mode + ".js";
        script.onerror = function loadError(err) {
          throw new URIError("Ace config " + err.target.src + " not found. ");
        };
        script.onload = () => {
          Mode = ace.require("ace/mode/" + mode).Mode;
          this.editor.session.setMode(new Mode());
        }

        document.head.appendChild(script);
      }
    }

    aceRequire(modulePath) {
      return new Promise(function(resolve, fail) {
        var module = ace.require(modulePath);
        if (module) {
          resolve(module)
        } else {
          var realPath = modulePath.replace(/^(ace\/[a-z]+)\//,"$1-")
          var script = document.createElement("Script");
          script.type = "text/javascript";
          script.src = lively4url + "/src/external/" + realPath + ".js";
          script.onload = function() {
            var module = ace.require(modulePath);
            if(module)
              resolve(module)
            else
              fail("ace-editor: could not load " + modulePath + " ("+ realPath +")" )
          }
          document.head.appendChild(script);
        }
      })
    }

    changeTheme(theme) {
        this.editor.setTheme("ace/theme/" + theme);
    }

    // var editor = editor = $("lively-editor")[0].shadowRoot.querySelector("#editor").editor;
    customizeEditor() {
        var editor = this.editor;

        editor.currentSelectionOrLine = function() {
            let text,
                sel =  this.getSelectionRange();
            if (sel.start.row == sel.end.row && sel.start.column == sel.end.column) {
                var currline = this.getSelectionRange().start.row;
                text = this.session.getLine(currline);
            } else {
                text = this.getCopyText()
            };
            return text
        }

        editor.commands.addCommand({
            name: "doIt",
            bindKey: {win: "Ctrl-D", mac: "Command-D"},
            exec: (editor) => {
                let text = editor.currentSelectionOrLine()
                let result = this.tryBoundEval(text);
                lively.notify("" + result)
            }
        })

        editor.commands.addCommand({
            name: "printIt",
            bindKey: {win: "Ctrl-P", mac: "Command-P"},
            exec: (editor) => {
                let text = editor.currentSelectionOrLine()
                let result = this.tryBoundEval(text, true);
            }
        });

        editor.commands.addCommand({
            name: "inspectIt",
            bindKey: {win: "Ctrl-I", mac: "Command-I"},
            exec: (editor) => {
                let text = editor.currentSelectionOrLine()
                let result = this.inspectIt(text, true);
            }
        });

        editor.commands.addCommand({
            name: "doSave",
            bindKey: {win: "Ctrl-S", mac: "Command-S"},
            exec: (editor) => {
                this.doSave(this.editor.getValue());
            }
        });
    };

    getDoitContext() {
        return this.doitContext
    }

    boundEval(str) {
      // just a hack... to get rid of some async....
      // #TODO make this more general
      // works: await new Promise((r) => r(3))
      // does not work yet: console.log(await new Promise((r) => r(3)))
      if (str.match(/^await /)) {
        str = "(async () => window._ = " + str +")()"
      }

      var ctx = this.getDoitContext() || this,
          interactiveEval = function(text) { return eval(text) };
      // #TODO binding "this" does not work yet, this is needed to build interactive debuggers... and cool workspaces
      var transpiledSource = babel.transform(str, {stage: 0}).code
      // #TODO alt: babel.run
      var result =  interactiveEval.call(ctx, transpiledSource);

      return result
    }

    printResult(result) {
        var editor = this.editor;
        var text = result
        var fromSel =  editor.getSelectionRange().end;
        editor.selection.moveCursorToPosition(fromSel)
        editor.selection.clearSelection() // don't replace existing selection
        editor.insert(text)
        var toSel =  editor.getSelectionRange().start;
        editor.selection.moveCursorToPosition(fromSel)
        editor.selection.selectToPosition(toSel)
    }

    tryBoundEval(str, printResult) {
        var result;
        try { result =  this.boundEval(str) }
        catch(e) {
            document.LastError = e
            console.log("Error: " + e)
            result = e
        }
        if (printResult) {
            if (str.match(/^await/) && result.then) {
              // we will definitly return a promise on which we can wait here
              result.then( result => this.printResult("" +result))
            } else {
              this.printResult(" " +result)
            }
        }
        return result
    }

    inspectIt(str) {
        var result;
        try { result =  this.boundEval(str) }
        catch(e) {
            document.LastError = e
            console.log("Error: " + e)
            result = e
        }
        var result = lively.inspector.openInspector(result)
        return result
    }

    doSave(text) {
        this.tryBoundEval(text) // just a default implementation...
    }

    onResize() {
      this.editor.resize();
      console.log("resizing");
    }

    // document.registerElement('juicy-ace-editor', {
    //     prototype: TomalecAceEditorPrototype
    // });

    // Fires when an instance of the element is created
    createdCallback() {
        console.log("createdCallback")
        var value = "";
        Object.defineProperty(this, "value", {
            set: function(val){
                this.editor && this.editor.setValue( val );
                value = val;
            },
            get: function(){
                value = this.editor && this.editor.getValue() || value;
                return value;
            }
        });

        // Creates the shadow root
        // var shadowRoot = this.createShadowRoot();

        // Adds a template clone into shadow root
        // var clone = thatDoc.importNode(template, true);
        this.container = this.shadowRoot.getElementById("juicy-ace-editor-container");
        // shadowRoot.appendChild(clone);

        // this.dispatchEvent(new Event("created")); // already taken care...
    };

    loadSpellcheck() {
      this.contents_modified = true;
      this.currently_spellchecking = false;
      this.markers_present = [];

      this.dictionary = null;

      var lang = "en_US";
      var dicPath = lively4url + "/src/external/dictionaries/en_US/en_US.dic";
      var affPath = lively4url + "/src/external/dictionaries/en_US/en_US.aff";

      var affData;
      var dicData;

      // Load the dictionary.
      // We have to load the dictionary files sequentially to ensure
      lively.import("typo").then(() => {
        $.get(dicPath, (data) => {
        	dicData = data;
        }).done(() => {
          $.get(affPath, (data) =>{
        	  affData = data;
          }).done(() => {
          	console.log("Dictionary loaded");
            this.dictionary = new lively.typo(lang, affData, dicData);
            this.enableSpellcheck();
            this.spellCheck();
          });
        });
      })
    };

    // Check the spelling of a line, and return [start, end]-pairs for misspelled words.
    misspelled (line) {
      	var words = line.split(' ');
      	var i = 0;
      	var bads = [];
        var word;
      	for (word in words) {
      	  var x = words[word] + "";
      	  var checkWord = x.replace(/[^a-zA-Z']/g, '');
      	  if (!this.dictionary.check(checkWord)) {
      	    bads[bads.length] = [i, i + words[word].length];
      	  }
      	  i += words[word].length + 1;
        }
        return bads;
      }

    enableSpellcheck () {
        this.editor.getSession().on('change', (e) => {
        	this.contents_modified = true;
      	});
      	setInterval(() => { this.spellCheck()}, 500);
      }

    // Spell check the Ace editor contents.
    spellCheck() {
        // Wait for the dictionary to be loaded.
        if (this.dictionary == null) {
          return;
        }

        if (this.currently_spellchecking) {
        	return;
        }

        if (!this.contents_modified) {
        	return;
        }

        console.log("spell check!")

        this.currently_spellchecking = true;
        var session = this.editor.getSession();

        // Clear the markers.
        for (var i in this.markers_present) {
          session.removeMarker(this.markers_present[i]);
        }
        this.markers_present = [];

        try {
      	  var Range = ace.require('ace/range').Range
      	  var lines = session.getDocument().getAllLines();
      	  console.log("lines: " + lines)
      	  for (var i in lines) {
      	  	// Clear the gutter.
      	    session.removeGutterDecoration(i, "misspelled");
      	    // Check spelling of this line.
      	    var misspellings = this.misspelled(lines[i]);

      	    // Add markers and gutter markings.
      	    if (misspellings.length > 0) {
      	      session.addGutterDecoration(i, "misspelled");
      	    }
      	    for (var j in misspellings) {
      	      var range = new Range(i, misspellings[j][0], i, misspellings[j][1]);
      	      this.markers_present[this.markers_present.length] =
      	        session.addMarker(range, "misspelled", "typo", true);
      	    }
      	  }
      	} finally {
      		this.currently_spellchecking = false;
      		this.contents_modified = false;
      	}
      }
}

<commit_msg>SYNC  M templates/classes/AceEditor.js;<commit_after>

export default class AceEditor extends HTMLElement {

    // Fires when an instance was inserted into the document
    attachedCallback() {
        var text = this.childNodes[0];
        var container = this.container;
        var element = this;

        if(this.editor){
            var editor = this.editor;
            this.value = text.textContent || this.value;
        } else {
            // container.appendChild(text);
            container.innerHTML = this.innerHTML || this.value;
            editor = ace.edit(container);
            this.dispatchEvent(new CustomEvent("editor-ready", {detail: editor}));
            this.editor = editor;

            // inject base editor styles
            this.injectTheme('#ace_editor\\.css');
            this.injectTheme('#ace-tm');

            editor.getSession().on('change', function(event){
                element.dispatchEvent(new CustomEvent("change", {detail: event}));
            });
        }

        // handle theme changes
        editor.renderer.addEventListener("themeLoaded", this.onThemeLoaded.bind(this));

        // initial attributes
            editor.setTheme( this.getAttribute("theme") );
            editor.setFontSize( this.getAttribute("fontsize") );
            editor.setReadOnly( this.getAttribute("readonly") );
            var session = editor.getSession();
            session.setMode( this.getAttribute("mode") );
            session.setUseSoftTabs( this.getAttribute("softtabs") );
            this.getAttribute("tabsize") && session.setTabSize( this.getAttribute("tabsize") );
            session.setUseWrapMode( this.hasAttribute("wrapmode") );

        // prevent error message when CTRL+P
        editor.$blockScrolling = Infinity

        // Observe input textNode changes
        // Could be buggy as editor was also added to Light DOM;
            var observer = new MutationObserver(function(mutations) {
              mutations.forEach(function(mutation) {
                // console.log("observation", mutation.type, arguments, mutations, editor, text);
                if(mutation.type == "characterData"){
                    element.value = text.data;
                }
              });
            });
            text && observer.observe(text, { characterData: true });
        // container.appendChild(text);
        this._attached = true;

        this.customizeEditor();

        this.loadSpellcheck()
        this.enableSpellcheck()

        // editor.resize()
        window.setTimeout(() => {
          this.editor.resize();
        }, 50) // #Hack #Resize #RaceCondition

        var _this = this;
        window.setTimeout(() => {
          // we also use this timeout hack, but we should get rid of it!!
          new ResizeSensor($(_this.container), () => {
            this.editor.resize();
          });
        }, 500);
    };

    // Fires when an instance was removed from the document
    detachedCallback() {
        this._attached = false;
    };

    // Fires when an attribute was added, removed, or updated
    attributeChangedCallback(attr, oldVal, newVal) {
        if(!this._attached){
            return false;
        }
        switch(attr){
            case "theme":
                this.editor.setTheme( newVal );
                break;
            case "mode":
                this.editor.getSession().setMode( newVal );
                break;
            case "fontsize":
                this.editor.setFontSize( newVal );
                break;
            case "softtabs":
                this.editor.getSession().setUseSoftTabs( newVal );
                break;
            case "tabsize":
                this.editor.getSession().setTabSize( newVal );
                break;
            case "readonly":
                this.editor.setReadOnly( newVal );
                break;
            case "wrapmode":
                this.editor.getSession().setUseWrapMode( newVal !== null );
                break;

        }
    };

    onThemeLoaded(e){
        var themeId = "#" + e.theme.cssClass;
        this.injectTheme(themeId);
        // Workaround Chrome stable bug, force repaint
        this.container.style.display='none';
        this.container.offsetHeight;
        this.container.style.display='';
    };

    // inject the style tag of a theme to the element
    injectTheme(themeId){

        //helper function to clone a style
        function cloneStyle(style) {
            var s = document.createElement('style');
            s.id = style.id;
            s.textContent = style.textContent;
            return s;
        }

        var n = document.querySelector(themeId);
        this.shadowRoot.appendChild(cloneStyle(n));
    };

    // CUSTOMIZATION
    enableAutocompletion(filename) {
      return this.aceRequire("ace/ext/language_tools").then( module => {
        this.editor.setOptions({
            enableBasicAutocompletion: true,
            enableSnippets: true,
            enableLiveAutocompletion: false
        });
        this.editor.completers[3] =  {
          getCompletions: function(editor, session, pos, prefix, callback) {
              // console.log("getCompletions: " + pos + " prefix " + prefix)
              var curLine = session.getDocument().getLine(pos.row);
              var curTokens = curLine.slice(0, pos.column).split(/\s+/);
              var curCmd = _.last(curTokens);
              // console.log("line : " + curLine + " curTokens: " + curTokens + " curCmd:" + curCmd)
              if (!curCmd) return;
              try {
                var wordList = [];
              curCmd = curCmd.replace(/\.[^.]*$/,"")
              var obj = eval(curCmd);
              wordList = lively.allProperties(obj);
              // console.log("complete: " + curCmd +"\n" + wordList)
                callback(null, _.keys(wordList).map(function(ea) {
                  return {name: ea, value: ea, score: 300, meta: wordList[ea]};
                  }));
              } catch(err) {
                console.log("Could not complete: " + curCmd +", because of:" + err)
              }
            }
          }
      })
    }
    changeModeForFile(filename) {
      var modelist = ace.require("ace/ext/modelist");
      var mode = modelist.getModeForPath(filename).name;
      console.log(filename + " -> " + mode);

      this.changeMode(mode);

    }

    changeMode(mode) {
      var Mode;
      try {
        Mode = ace.require("ace/mode/" + mode).Mode;
        this.editor.session.setMode(new Mode());
      } catch (e) {
        console.log("ace-editor: lazy load ace mode " + mode)
        // mode is not loaded, so try to load it,
        // there must be a way to make ace do all this stuff as with themes... :(
        var script = document.createElement("Script");
        script.type = "text/javascript";
        script.src = lively4url + "/src/external/ace/mode-" + mode + ".js";
        script.onerror = function loadError(err) {
          throw new URIError("Ace config " + err.target.src + " not found. ");
        };
        script.onload = () => {
          Mode = ace.require("ace/mode/" + mode).Mode;
          this.editor.session.setMode(new Mode());
        }

        document.head.appendChild(script);
      }
    }

    aceRequire(modulePath) {
      return new Promise(function(resolve, fail) {
        var module = ace.require(modulePath);
        if (module) {
          resolve(module)
        } else {
          var realPath = modulePath.replace(/^(ace\/[a-z]+)\//,"$1-")
          var script = document.createElement("Script");
          script.type = "text/javascript";
          script.src = lively4url + "/src/external/" + realPath + ".js";
          script.onload = function() {
            var module = ace.require(modulePath);
            if(module)
              resolve(module)
            else
              fail("ace-editor: could not load " + modulePath + " ("+ realPath +")" )
          }
          document.head.appendChild(script);
        }
      })
    }

    changeTheme(theme) {
        this.editor.setTheme("ace/theme/" + theme);
    }

    // var editor = editor = $("lively-editor")[0].shadowRoot.querySelector("#editor").editor;
    customizeEditor() {
        var editor = this.editor;

        editor.currentSelectionOrLine = function() {
            let text,
                sel =  this.getSelectionRange();
            if (sel.start.row == sel.end.row && sel.start.column == sel.end.column) {
                var currline = this.getSelectionRange().start.row;
                text = this.session.getLine(currline);
            } else {
                text = this.getCopyText()
            };
            return text
        }

        editor.commands.addCommand({
            name: "doIt",
            bindKey: {win: "Ctrl-D", mac: "Command-D"},
            exec: (editor) => {
                let text = editor.currentSelectionOrLine()
                let result = this.tryBoundEval(text);
                lively.notify("" + result)
            }
        })

        editor.commands.addCommand({
            name: "printIt",
            bindKey: {win: "Ctrl-P", mac: "Command-P"},
            exec: (editor) => {
                let text = editor.currentSelectionOrLine()
                let result = this.tryBoundEval(text, true);
            }
        });

        editor.commands.addCommand({
            name: "inspectIt",
            bindKey: {win: "Ctrl-I", mac: "Command-I"},
            exec: (editor) => {
                let text = editor.currentSelectionOrLine()
                let result = this.inspectIt(text, true);
            }
        });

        editor.commands.addCommand({
            name: "doSave",
            bindKey: {win: "Ctrl-S", mac: "Command-S"},
            exec: (editor) => {
                this.doSave(this.editor.getValue());
            }
        });
    };

    getDoitContext() {
        return this.doitContext
    }

    boundEval(str) {
      // just a hack... to get rid of some async....
      // #TODO make this more general
      // works: await new Promise((r) => r(3))
      // does not work yet: console.log(await new Promise((r) => r(3)))
      if (str.match(/^await /)) {
        str = "(async () => window._ = " + str +")()"
      }

      var ctx = this.getDoitContext() || this,
          interactiveEval = function(text) { return eval(text) };
      // #TODO binding "this" does not work yet, this is needed to build interactive debuggers... and cool workspaces
      var transpiledSource = babel.transform(str, {stage: 0}).code
      // #TODO alt: babel.run
      var result =  interactiveEval.call(ctx, transpiledSource);

      return result
    }

    printResult(result) {
        var editor = this.editor;
        var text = result
        var fromSel =  editor.getSelectionRange().end;
        editor.selection.moveCursorToPosition(fromSel)
        editor.selection.clearSelection() // don't replace existing selection
        editor.insert(text)
        var toSel =  editor.getSelectionRange().start;
        editor.selection.moveCursorToPosition(fromSel)
        editor.selection.selectToPosition(toSel)
    }

    tryBoundEval(str, printResult) {
        var result;
        try { result =  this.boundEval(str) }
        catch(e) {
            document.LastError = e
            console.log("Error: " + e)
            result = e
        }
        if (printResult) {
            if (str.match(/^await/) && result.then) {
              // we will definitly return a promise on which we can wait here
              result.then( result => this.printResult("" +result))
            } else {
              this.printResult(" " +result)
            }
        }
        return result
    }

    inspectIt(str) {
        var result;
        try { result =  this.boundEval(str) }
        catch(e) {
            document.LastError = e
            console.log("Error: " + e)
            result = e
        }
        var result = lively.inspector.openInspector(result)
        return result
    }

    doSave(text) {
        this.tryBoundEval(text) // just a default implementation...
    }

    onResize() {
      this.editor.resize();
      console.log("resizing");
    }

    // document.registerElement('juicy-ace-editor', {
    //     prototype: TomalecAceEditorPrototype
    // });

    // Fires when an instance of the element is created
    createdCallback() {
        console.log("createdCallback")
        var value = "";
        Object.defineProperty(this, "value", {
            set: function(val){
                this.editor && this.editor.setValue( val );
                value = val;
            },
            get: function(){
                value = this.editor && this.editor.getValue() || value;
                return value;
            }
        });

        // Creates the shadow root
        // var shadowRoot = this.createShadowRoot();

        // Adds a template clone into shadow root
        // var clone = thatDoc.importNode(template, true);
        this.container = this.shadowRoot.getElementById("juicy-ace-editor-container");
        // shadowRoot.appendChild(clone);

        // this.dispatchEvent(new Event("created")); // already taken care...
    };

    loadSpellcheck() {
      this.contents_modified = true;
      this.currently_spellchecking = false;
      this.markers_present = [];

      this.dictionary = null;

      var lang = "en_US";
      var dicPath = lively4url + "/src/external/dictionaries/en_US/en_US.dic";
      var affPath = lively4url + "/src/external/dictionaries/en_US/en_US.aff";

      var affData;
      var dicData;

      // Load the dictionary.
      // We have to load the dictionary files sequentially to ensure
      lively.import("typo").then(() => {
        $.get(dicPath, (data) => {
        	dicData = data;
        }).done(() => {
          $.get(affPath, (data) =>{
        	  affData = data;
          }).done(() => {
          	console.log("Dictionary loaded");
            this.dictionary = new lively.typo(lang, affData, dicData);
            this.enableSpellcheck();
            this.spellCheck();
          });
        });
      })
    };

    // Check the spelling of a line, and return [start, end]-pairs for misspelled words.
    misspelled (line) {
      	var words = line.split(' ');
      	var i = 0;
      	var bads = [];
        var word;
      	for (word in words) {
      	  var x = words[word] + "";
      	  var checkWord = x.replace(/[^a-zA-Z']/g, '');
      	  if (!this.dictionary.check(checkWord)) {
      	    bads[bads.length] = [i, i + words[word].length];
      	  }
      	  i += words[word].length + 1;
        }
        return bads;
      }

    enableSpellcheck () {
        this.editor.getSession().on('change', (e) => {
        	this.contents_modified = true;
      	});
      	setInterval(() => { this.spellCheck()}, 500);
      }

    // Spell check the Ace editor contents.
    spellCheck() {
        // Wait for the dictionary to be loaded.
        if (this.dictionary == null) {
          return;
        }

        if (this.currently_spellchecking) {
        	return;
        }

        if (!this.contents_modified) {
        	return;
        }

        console.log("spell check!")

        this.currently_spellchecking = true;
        var session = this.editor.getSession();

        // Clear the markers.
        for (var i in this.markers_present) {
          session.removeMarker(this.markers_present[i]);
        }
        this.markers_present = [];

        try {
      	  var Range = ace.require('ace/range').Range
      	  var lines = session.getDocument().getAllLines();
      	  for (var i in lines) {
      	  	// Clear the gutter.
      	    session.removeGutterDecoration(i, "misspelled");
      	    // Check spelling of this line.
      	    var misspellings = this.misspelled(lines[i]);

      	    // Add markers and gutter markings.
      	    if (misspellings.length > 0) {
      	      session.addGutterDecoration(i, "misspelled");
      	    }
      	    for (var j in misspellings) {
      	      var range = new Range(i, misspellings[j][0], i, misspellings[j][1]);
      	      // console.log("missspell: ", misspellings[j])

      	      this.markers_present[this.markers_present.length] =
      	        session.addMarker(range, "misspelled", "typo", true);
      	    }
      	  }
      	} finally {
      		this.currently_spellchecking = false;
      		this.contents_modified = false;
      	}
      }
}

<|endoftext|>


<commit_before>/**
 * ApiController.test.js
 */
var request = require('supertest');
var should = require('should');

/**
 * Test API "ping" endpoint.
 */
describe('Requests to the root (/api) path', function() {

  it('GET: Returns a 200 status code', function (done) {
    request(sails.hooks.http.app)
      .get('/api')
      .expect(200, done);
  });

  it('GET: Returns JSON format', function(done) {
    request(app)
      .get('/api')
      .expect("content-type", /json/, done)
  });

});

/**
 * Test API V1 information / status endpoint.
 */
describe('Requests to v1 root (/api/v1) path', function() {

  it('GET: Returns a 200 status code', function(done) {

    request(sails.hooks.http.app)
      .get('/api/v1')
      .expect(200, done);
  });

  it('GET: Returns JSON format', function(done) {

    request(sails.hooks.http.app)
      .get('/api/v1')
      .expect("content-type", /json/, done)
  });
});
<commit_msg>Fixed request call<commit_after>/**
 * ApiController.test.js
 */
var request = require('supertest');
var should = require('should');

/**
 * Test API "ping" endpoint.
 */
describe('Requests to the root (/api) path', function() {

  it('GET: Returns a 200 status code', function (done) {
    request(sails.hooks.http.app)
      .get('/api')
      .expect(200, done);
  });

  it('GET: Returns JSON format', function(done) {
    request(sails.hooks.http.app)
      .get('/api')
      .expect("content-type", /json/, done)
  });

});

/**
 * Test API V1 information / status endpoint.
 */
describe('Requests to v1 root (/api/v1) path', function() {

  it('GET: Returns a 200 status code', function(done) {

    request(sails.hooks.http.app)
      .get('/api/v1')
      .expect(200, done);
  });

  it('GET: Returns JSON format', function(done) {

    request(sails.hooks.http.app)
      .get('/api/v1')
      .expect("content-type", /json/, done)
  });
});
<|endoftext|>


<commit_before>/* eslint unicorn/filename-case: "off", func-names: "off", camelcase: "off", no-unused-vars: "off" */

module.exports = function (iface) {
    const {mqttPub, mqttSub, mqttStatus, log, newAccessory, Service, Characteristic} = iface;

    return function createAccessory_Speaker(settings) {
        const speaker = newAccessory(settings);

        speaker.addService(Service.Speaker, settings.name)
            .getCharacteristic(Characteristic.Mute)
            .on('set', (value, callback) => {
                log.debug('< hap set', settings.name, 'Mute', value);
                const mute = value ? settings.payload.muteTrue : settings.payload.muteFalse;
                log.debug('> mqtt', settings.topic.setOn, mute);
                mqttPub(settings.topic.setMute, mute);
                callback();
            });

        // Update status in homekit if exernal status gets updated
        mqttSub(settings.topic.statusMute, val => {
            const mute = val !== settings.topic.muteFalse;
            log.debug('> hap update', settings.name, 'Mute', mute);
            speaker.getService(Service.Speaker)
                .updateCharacteristic(Characteristic.Mute, mute);
        });

        speaker.getService(Service.Speaker)
            .getCharacteristic(Characteristic.Mute)
            .on('get', callback => {
                log.debug('< hap get', settings.name, 'Mute');
                const mute = mqttStatus[settings.topic.statusMute] !== settings.payload.muteFalse;
                log.debug('> hap re_get', settings.name, 'Mute', mute);
                callback(null, mute);
            });

        if (settings.topic.setVolume) {
            speaker.getService(Service.Speaker)
                .addCharacteristic(Characteristic.Volume)
                .on('set', (value, callback) => {
                    log.debug('< hap set', settings.name, 'Volume', value);
                    const volume = (value * (settings.payload.volumeFactor || 1)) || 0;
                    log.debug('> mqtt', settings.topic.setVolume, volume);
                    mqttPub(settings.topic.setVolume, volume);
                    callback();
                });

            if (settings.topic.statusVolume) {
                mqttSub(settings.topic.statusVolume, val => {
                    log.debug('> hap update', settings.name, 'Volume', mqttStatus[settings.topic.statusVolume]);
                    speaker.getService(Service.Speaker)
                        .updateCharacteristic(Characteristic.Volume);
                });

                speaker.getService(Service.Speaker)
                    .getCharacteristic(Characteristic.Volume)
                    .on('get', callback => {
                        log.debug('< hap get', settings.name, 'Volume');
                        const volume = (mqttStatus[settings.topic.statusVolume] / (settings.payload.volumeFactor || 1)) || 0;

                        log.debug('> hap re_get', settings.name, 'Volume', volume);
                        callback(null, volume);
                    });
            }
        }

        return speaker;
    };
};
<commit_msg>fix #35<commit_after>/* eslint unicorn/filename-case: "off", func-names: "off", camelcase: "off", no-unused-vars: "off" */

module.exports = function (iface) {
    const {mqttPub, mqttSub, mqttStatus, log, newAccessory, Service, Characteristic} = iface;

    return function createAccessory_Speaker(settings) {
        const speaker = newAccessory(settings);

        speaker.addService(Service.Speaker, settings.name)
            .getCharacteristic(Characteristic.Mute)
            .on('set', (value, callback) => {
                log.debug('< hap set', settings.name, 'Mute', value);
                const mute = value ? settings.payload.muteTrue : settings.payload.muteFalse;
                log.debug('> mqtt', settings.topic.setOn, mute);
                mqttPub(settings.topic.setMute, mute);
                callback();
            });

        if (settings.topic.statusMute) {
            // Update status in homekit if exernal status gets updated
            mqttSub(settings.topic.statusMute, val => {
                const mute = val !== settings.topic.muteFalse;
                log.debug('> hap update', settings.name, 'Mute', mute);
                speaker.getService(Service.Speaker)
                    .updateCharacteristic(Characteristic.Mute, mute);
            });
        }

        speaker.getService(Service.Speaker)
            .getCharacteristic(Characteristic.Mute)
            .on('get', callback => {
                log.debug('< hap get', settings.name, 'Mute');
                const mute = mqttStatus[settings.topic.statusMute] !== settings.payload.muteFalse;
                log.debug('> hap re_get', settings.name, 'Mute', mute);
                callback(null, mute);
            });

        if (settings.topic.setVolume) {
            speaker.getService(Service.Speaker)
                .addCharacteristic(Characteristic.Volume)
                .on('set', (value, callback) => {
                    log.debug('< hap set', settings.name, 'Volume', value);
                    const volume = (value * (settings.payload.volumeFactor || 1)) || 0;
                    log.debug('> mqtt', settings.topic.setVolume, volume);
                    mqttPub(settings.topic.setVolume, volume);
                    callback();
                });

            if (settings.topic.statusVolume) {
                mqttSub(settings.topic.statusVolume, val => {
                    log.debug('> hap update', settings.name, 'Volume', mqttStatus[settings.topic.statusVolume]);
                    speaker.getService(Service.Speaker)
                        .updateCharacteristic(Characteristic.Volume);
                });

                speaker.getService(Service.Speaker)
                    .getCharacteristic(Characteristic.Volume)
                    .on('get', callback => {
                        log.debug('< hap get', settings.name, 'Volume');
                        const volume = (mqttStatus[settings.topic.statusVolume] / (settings.payload.volumeFactor || 1)) || 0;

                        log.debug('> hap re_get', settings.name, 'Volume', volume);
                        callback(null, volume);
                    });
            }
        }

        return speaker;
    };
};
<|endoftext|>


<commit_before>/* globals define */
define(function(require, exports, module) {
  'use strict';

  var MoveableView = require('../base/MoveableView');
  var MoveableSurface = require('../base/MoveableSurface');
  var Transform = require('famous/core/Transform');
  var Utils = require('../base/Utils');

  function Layout (options) {
    MoveableView.apply(this, arguments);
    this._initialized = false;
    this._childSizes = [];
    this._children = this.options.children;
    this._activeLayout = this.options.layout;

    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      this._hideChild(child);
      this.bindEvents(child);
      this.add(child);
    }

    this._listenForSize();
  }

  Layout.prototype = Object.create(MoveableView.prototype);
  Layout.prototype.constructor = Layout;

  Layout.prototype.destroy = function () {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      this.unbindEvents(child);
    }
  }

  Layout.prototype.layout = function () {
    this._readSizes();
    if (this._activeLayout) {
      this._activeLayout.layout(this._children, this._childSizes);
    }
    return this;
  }

  Layout.prototype.bindEvents = function (elem) {};

  Layout.prototype.unbindEvents = function (elem) {};

  Layout.prototype.getSize = function () {
    if (this._initialized && this._activeLayout) {
      return this._activeLayout.getSize(
        this._children, this._childSizes);
    }
    else return undefined;
  }

  Layout.DEFAULT_OPTIONS = {
  };

  Layout.prototype._listenForSize = function (child, anim) {
    var self = this;
    Utils.when(function () {
      for (var i = 0; i < self._children.length; i++) {
        var child = self._children[i];
        var size = child.getSize();
        if (!size || size[0] === true || size[1] === true || size[0] === 0 || size[1] === 0) {
          return false;
        }
        return true;
      }
    }, this._sizeSafe.bind(this));
  }

  Layout.prototype._sizeSafe = function (child, anim) {
    this._initialized = true;
    this.setSize(this.getSize.bind(this));
    this.layout();
  }

  Layout.prototype._readSizes = function () {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      this._childSizes[i] = child.getSize().slice(0);
    }
  }

  Layout.prototype._hideChild = function (child, anim) {
    child.setOpacity(0, anim);
    child.setTransform(Transform.scale(0.001, 0.001), anim);
  }

  module.exports = Layout;
});
<commit_msg>showChild / bind events including index<commit_after>/* globals define */
define(function(require, exports, module) {
  'use strict';

  var MoveableView = require('../base/MoveableView');
  var MoveableSurface = require('../base/MoveableSurface');
  var Transform = require('famous/core/Transform');
  var Utils = require('../base/Utils');

  function Layout (options) {
    MoveableView.apply(this, arguments);
    this._initialized = false;
    this._childSizes = [];
    this._children = this.options.children;
    this._activeLayout = this.options.layout;

    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      this._hideChild(child);
      this.bindEvents(child, i);
      this.add(child);
    }

    this._listenForSize();
  }

  Layout.prototype = Object.create(MoveableView.prototype);
  Layout.prototype.constructor = Layout;

  Layout.prototype.destroy = function () {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      this.unbindEvents(child);
    }
  }

  Layout.prototype.layout = function () {
    this._readSizes();
    if (this._activeLayout) {
      this._activeLayout.layout(this._children, this._childSizes);
    }
    return this;
  }

  Layout.prototype.bindEvents = function (elem) {};

  Layout.prototype.unbindEvents = function (elem) {};

  Layout.prototype.getSize = function () {
    if (this._initialized && this._activeLayout) {
      return this._activeLayout.getSize(
        this._children, this._childSizes);
    }
    else return undefined;
  }

  Layout.DEFAULT_OPTIONS = {
  };

  Layout.prototype._listenForSize = function (child, anim) {
    var self = this;
    Utils.when(function () {
      for (var i = 0; i < self._children.length; i++) {
        var child = self._children[i];
        var size = child.getSize();
        if (!size || size[0] === true || size[1] === true || size[0] === 0 || size[1] === 0) {
          return false;
        }
        return true;
      }
    }, this._sizeSafe.bind(this));
  }

  Layout.prototype._sizeSafe = function (child, anim) {
    this._initialized = true;
    this.setSize(this.getSize.bind(this));
    this.layout();
  }

  Layout.prototype._readSizes = function () {
    for (var i = 0; i < this._children.length; i++) {
      var child = this._children[i];
      this._childSizes[i] = child.getSize().slice(0);
    }
  }

  Layout.prototype._hideChild = function (child, anim) {
    child.setOpacity(0, anim);
    child.setTransform(Transform.scale(0.001, 0.001), anim);
  }

  Layout.prototype._showChild = function (child, anim) {
    child.setOpacity(1, anim);
    child.setTransform(Transform.identity, anim);
  }

  module.exports = Layout;
});
<|endoftext|>


<commit_before>var fs = require('fs');
var nodePath = require('path');

var gimletApi = module.exports = {
  init: function() {
    if (files.inRepo()) { return; }

    files.createFilesFromTree({
      ".gimlet": {
        HEAD: "ref: refs/heads/master\n",
        index: "",
        objects: {},
        refs: {
          heads: {},
          remotes: {
            origin: {}
          },
        }
      }
    });
  },

  add: function(path) {
    files.assertInRepo();

    var addedFiles = files.lsRecursive(path);
    if (addedFiles.length === 0) {
      throw "fatal: pathspec '" + files.pathFromRepoRoot(path) + "' did not match any files";
    } else {
      for (var i = 0; i < addedFiles.length; i++) {
        this.update_index(addedFiles[i], { add: true });
      }
    }
  },

  update_index: function(path, opts) {
    files.assertInRepo();
    opts = opts || {};

    var pathFromRoot = files.pathFromRepoRoot(path)
    if (!fs.existsSync(path)) {
      throw "error: " + pathFromRoot + ": does not exist\n" +
        "fatal: Unable to process path " + pathFromRoot;
    } else if (fs.statSync(path).isDirectory()) {
      throw "error: " + pathFromRoot + ": is a directory - add files inside instead\n" +
        "fatal: Unable to process path " + pathFromRoot;
    } else if (!index.readHasFile(path) && opts.add === undefined) {
      throw "error: " + pathFromRoot  +
        ": cannot add to the index - missing --add option?\n" +
        "fatal: Unable to process path " + pathFromRoot;
    } else {
      index.writeFile(path);
    }
  },

  hash_object: function(file, opts) {
    files.assertInRepo();
    opts = opts || {};

    if (!fs.existsSync(file)) {
      throw "fatal: Cannot open '" + file + "': No such file or directory"
    } else {
      var fileContents = files.read(file);
      if (opts.w) {
        return objects.write(fileContents);
      }

      return util.hash(fileContents);
    }
  },

  write_tree: function() {
    files.assertInRepo();
    return objects.writeTree(index.objToTree(index.strToObj(index.read())));
  },

  commit: function(opts) {
    files.assertInRepo();

    if (Object.keys(index.strToObj(index.read())).length === 0) {
      throw "# On branch master\n#\n# Initial commit\n#\n" +
        "nothing to commit (create/copy files and use 'git add' to track)";
    } else {
      var headHash = refs.readExistentHash("HEAD");
      var treeHash = this.write_tree();

      if (headHash !== undefined &&
          treeHash === objects.readTreeHash(objects.read(headHash))) {
        throw "# On " + head.currentBranchName() + "\n" +
          "nothing to commit, working directory clean";
      } else {
        var isFirstCommit = refs.readExistentHash("HEAD") === undefined;
        var parentHashes = isFirstCommit ? [] : [refs.readExistentHash("HEAD")];
        var commmitHash = objects.write(objects.commitContent(treeHash, opts.m, parentHashes));
        this.update_ref("HEAD", commmitHash);
        return "[" + head.currentBranchName() + " " + commmitHash + "] " + opts.m;
      }
    }
  },

  branch: function(name) {
    files.assertInRepo();

    if (name === undefined) {
      return refs.readLocalHeads().map(function(branchName) {
        var marker = branchName === head.currentBranchName() ? "* " : "  ";
        return marker + branchName;
      }).join("\n") + "\n";
    } else if (refs.readExistentHash("HEAD") === undefined) {
      throw "fatal: Not a valid object name: '" + head.currentBranchName() + "'.";
    } else {
      refs.write(refs.nameToBranchRef(name), refs.readExistentHash("HEAD"));
    }
  },

  update_ref: function(refToUpdate, refToUpdateTo) {
    files.assertInRepo();

    if (!refs.isRef(refToUpdate)) {
      throw "fatal: Cannot lock the ref " + refToUpdate + ".";
    } else {
      var hash = refs.readExistentHash(refToUpdateTo);
      if (!objects.readExists(hash)) {
        throw "fatal: " + refToUpdateTo + ": not a valid SHA1";
      } else if (!(objects.type(objects.read(hash)) === "commit")) {
        throw "error: Trying to write non-commit object " + hash + " to branch " +
          refs.toLocalHead(refToUpdate) + "\n" +
          "fatal: Cannot update the ref " + refToUpdate;
      } else {
        refs.write(refs.toLocalHead(refToUpdate), hash);
      }
    }
  },

  checkout: function(ref) {
    files.assertInRepo();

    var finalRef = refs.isRef(ref) ? ref : refs.toFinalRef(ref);
    var hash = refs.toHash(finalRef);

    if (!objects.readExists(hash)) {
      throw "error: pathspec " + ref + " did not match any file(s) known to git."
    }
  },

  diff: function(ref1, ref2, opts) {
    files.assertInRepo();

    if (opts["name-only"] !== true) {
      throw "unsupported"; // for now
    }
  }
};

var head = {
  currentBranchName: function() {
    if (this.read().match("refs")) {
      return this.read().match("refs/heads/(.+)")[1];
    }
  },

  read: function() {
    var content = files.read(nodePath.join(files.gimletDir(), "HEAD"));
    var refMatch = content.match("ref: (refs/heads/.+)");
    return refMatch ? refMatch[1] : content;
  },

  write: function(ref) {
    if (refs.isLocalHeadRef(ref)) {
      files.write(nodePath.join(files.gimletDir(), "HEAD"), "ref: " + ref + "\n");
    }
  }
};

var refs = {
  isLocalHeadRef: function(ref) {
    return ref.match("refs/heads/[A-Za-z-]+");
  },

  isRef: function(ref) {
    return ref === "HEAD" || this.isLocalHeadRef(ref);
  },

  toLocalHead: function(ref) {
    if (ref === "HEAD") {
      return head.read();
    } else if (this.isLocalHeadRef(ref)) {
      return ref;
    } else {
      return this.nameToBranchRef(ref);
    }
  },

  readExistentHash: function(ref) {
    if (objects.readExists(ref)) {
      return ref;
    } else if (this.readExists(this.toLocalHead(ref))) {
      return files.read(nodePath.join(files.gimletDir(), this.toLocalHead(ref)));
    } else if (this.readExists(this.nameToBranchRef(ref))) {
      return files.read(nodePath.join(files.gimletDir(), this.nameToBranchRef(ref)));
    }
  },

  nameToBranchRef: function(name) {
    return "refs/heads/" + name;
  },

  write: function(ref, content) {
    if (this.isLocalHeadRef(ref)) {
      files.write(nodePath.join(files.gimletDir(), ref), content);
    }
  },

  readLocalHeads: function() {
    return fs.readdirSync(nodePath.join(files.gimletDir(), "refs/heads/"));
  },

  readExists: function(ref) {
    return ref !== undefined &&
      this.isLocalHeadRef(ref) &&
      fs.existsSync(nodePath.join(files.gimletDir(), ref));
  }
};

var index = {
  readHasFile: function(path) {
    return this.strToObj(this.read())[path] !== undefined;
  },

  read: function() {
    return files.read(nodePath.join(files.gimletDir(), "index"));
  },

  writeFile: function(path) {
    var index = this.strToObj(this.read());
    index[path] = util.hash(files.read(nodePath.join(files.repoDir(), path)));
    gimletApi.hash_object(path, { w: true });
    this.write(index);
  },

  strToObj: function(content) { // CHUCK THIS WHEN REFACTOR DONE
    return util.lines(content)
      .reduce(function(index, blobStr) {
        var blobData = blobStr.split(/ /);
        index[blobData[0]] = blobData[1];
        return index;
      }, {});
  },

  write: function(index) {
    var indexStr = Object.keys(index)
        .map(function(path) { return path + " " + index[path]; })
        .join("\n")
        .concat("\n"); // trailing new line
    files.write(nodePath.join(files.gimletDir(), "index"), indexStr);
  },

  objToTree: function(obj) {
    var tree = {};
    Object.keys(obj).forEach(function(wholePath) {
      util.assocIn(tree, wholePath.split(nodePath.sep).concat(files.read(wholePath)));
    });

    return tree;
  }
};

var objects = {
  writeTree: function(tree) {
    var treeObject = Object.keys(tree).map(function(key) {
      if (util.isString(tree[key])) {
        return "blob " + util.hash(tree[key]) + " " + key;
      } else {
        return "tree " + objects.writeTree(tree[key]) + " " + key;
      }
    }).join("\n") + "\n";

    return this.write(treeObject);
  },

  commitContent: function(treeHash, message, parentHashes) {
    return "commit " + treeHash + "\n" +
      parentHashes.map(function(h) { return "parent " + h + "\n"; }).join("") +
      "Date:  " + new Date().toString() + "\n" +
      "\n" +
      "    " + message;
  },

  write: function(content) {
    var contentHash = util.hash(content);
    if (this.read(contentHash) === undefined) {
      var filePath = nodePath.join(files.gimletDir(), "objects", contentHash);
      files.write(filePath, content);
    }

    return contentHash;
  },

  readExists: function(objectHash) {
    return objectHash !== undefined &&
      fs.existsSync(nodePath.join(files.gimletDir(), "objects", objectHash));
  },

  read: function(objectHash) {
    var objectPath = nodePath.join(files.gimletDir(), "objects", objectHash);
    if (fs.existsSync(objectPath)) {
      return files.read(objectPath);
    }
  },

  type: function(content) {
    var firstToken = content.split(" ")[0];
    if (firstToken === "commit") {
      return "commit";
    } else if (firstToken === "tree" || firstToken === "blob") {
      return "tree";
    } else {
      return "blob";
    }
  },

  readTreeHash: function(content) {
    if (this.type(content) === "commit") {
      return content.split(/\s/)[1];
    } else if (this.type(content) === "tree") {
      return hash(content);
    }
  }
};

var files = {
  gimletDir: function(dir) {
    if (dir === undefined) { return this.gimletDir(process.cwd()); }

    if (fs.existsSync(dir)) {
      var potentialGimletDir = nodePath.join(dir, ".gimlet");
      if (fs.existsSync(potentialGimletDir)) {
        return potentialGimletDir;
      } else if (dir !== "/") {
        return this.gimletDir(nodePath.join(dir, ".."));
      }
    }
  },

  repoDir: function() {
    if (this.gimletDir() !== undefined) {
      return nodePath.join(this.gimletDir(), "..")
    }
  },

  inRepo: function(cwd) {
    return this.gimletDir(cwd) !== undefined;
  },

  assertInRepo: function() {
    if (!this.inRepo()) {
      throw "fatal: Not a gimlet repository (or any of the parent directories): .gimlet";
    }
  },

  pathFromRepoRoot: function(path) {
    return nodePath.relative(this.repoDir(), nodePath.join(process.cwd(), path));
  },

  createFilesFromTree: function(structure, prefix) {
    if (prefix === undefined) {
      return files.createFilesFromTree(structure, process.cwd());
    }

    Object.keys(structure).forEach(function(name) {
      var path = nodePath.join(prefix, name);
      if (util.isString(structure[name])) {
        files.write(path, structure[name]);
      } else {
        fs.mkdirSync(path, "777");
        files.createFilesFromTree(structure[name], path);
      }
    });
  },

  read: function(path) {
    return fs.readFileSync(path, "utf8");
  },

  write: function(path, content) {
    fs.writeFileSync(path, content);
  },

  lsRecursive: function(path) {
    if (!fs.existsSync(path)) {
      return [];
    } else if (fs.statSync(path).isFile()) {
      return [path];
    } else if (fs.statSync(path).isDirectory()) {
      var self = this;
      return fs.readdirSync(path).reduce(function(files, dirChild) {
        return files.concat(self.lsRecursive(nodePath.join(path, dirChild)));
      }, []);
    }
  }
};

var util = {
  isString: function(thing) {
    return typeof thing === "string";
  },

  hash: function(string) {
    var hashInt = 0;
    for (var i = 0; i < string.length; i++) {
      hashInt = hashInt * 31 + string.charCodeAt(i);
      hashInt = hashInt | 0;
    }

    return Math.abs(hashInt).toString(16);
  },

  assocIn: function(obj, arr) {
    if (arr.length === 2) {
      obj[arr[0]] = arr[1];
    } else if (arr.length > 2) {
      obj[arr[0]] = obj[arr[0]] || {};
      this.assocIn(obj[arr[0]], arr.slice(1));
    }
  },

  lines: function(str) {
    return str.split("\n").slice(0, -1); // last is empty
  }
};
<commit_msg>head.currentBranchName -> head.readCurrentBranchName<commit_after>var fs = require('fs');
var nodePath = require('path');

var gimletApi = module.exports = {
  init: function() {
    if (files.inRepo()) { return; }

    files.createFilesFromTree({
      ".gimlet": {
        HEAD: "ref: refs/heads/master\n",
        index: "",
        objects: {},
        refs: {
          heads: {},
          remotes: {
            origin: {}
          },
        }
      }
    });
  },

  add: function(path) {
    files.assertInRepo();

    var addedFiles = files.lsRecursive(path);
    if (addedFiles.length === 0) {
      throw "fatal: pathspec '" + files.pathFromRepoRoot(path) + "' did not match any files";
    } else {
      for (var i = 0; i < addedFiles.length; i++) {
        this.update_index(addedFiles[i], { add: true });
      }
    }
  },

  update_index: function(path, opts) {
    files.assertInRepo();
    opts = opts || {};

    var pathFromRoot = files.pathFromRepoRoot(path)
    if (!fs.existsSync(path)) {
      throw "error: " + pathFromRoot + ": does not exist\n" +
        "fatal: Unable to process path " + pathFromRoot;
    } else if (fs.statSync(path).isDirectory()) {
      throw "error: " + pathFromRoot + ": is a directory - add files inside instead\n" +
        "fatal: Unable to process path " + pathFromRoot;
    } else if (!index.readHasFile(path) && opts.add === undefined) {
      throw "error: " + pathFromRoot  +
        ": cannot add to the index - missing --add option?\n" +
        "fatal: Unable to process path " + pathFromRoot;
    } else {
      index.writeFile(path);
    }
  },

  hash_object: function(file, opts) {
    files.assertInRepo();
    opts = opts || {};

    if (!fs.existsSync(file)) {
      throw "fatal: Cannot open '" + file + "': No such file or directory"
    } else {
      var fileContents = files.read(file);
      if (opts.w) {
        return objects.write(fileContents);
      }

      return util.hash(fileContents);
    }
  },

  write_tree: function() {
    files.assertInRepo();
    return objects.writeTree(index.objToTree(index.strToObj(index.read())));
  },

  commit: function(opts) {
    files.assertInRepo();

    if (Object.keys(index.strToObj(index.read())).length === 0) {
      throw "# On branch master\n#\n# Initial commit\n#\n" +
        "nothing to commit (create/copy files and use 'git add' to track)";
    } else {
      var headHash = refs.readExistentHash("HEAD");
      var treeHash = this.write_tree();

      if (headHash !== undefined &&
          treeHash === objects.readTreeHash(objects.read(headHash))) {
        throw "# On " + head.readCurrentBranchName() + "\n" +
          "nothing to commit, working directory clean";
      } else {
        var isFirstCommit = refs.readExistentHash("HEAD") === undefined;
        var parentHashes = isFirstCommit ? [] : [refs.readExistentHash("HEAD")];
        var commmitHash = objects.write(objects.commitContent(treeHash, opts.m, parentHashes));
        this.update_ref("HEAD", commmitHash);
        return "[" + head.readCurrentBranchName() + " " + commmitHash + "] " + opts.m;
      }
    }
  },

  branch: function(name) {
    files.assertInRepo();

    if (name === undefined) {
      return refs.readLocalHeads().map(function(branchName) {
        var marker = branchName === head.readCurrentBranchName() ? "* " : "  ";
        return marker + branchName;
      }).join("\n") + "\n";
    } else if (refs.readExistentHash("HEAD") === undefined) {
      throw "fatal: Not a valid object name: '" + head.readCurrentBranchName() + "'.";
    } else {
      refs.write(refs.nameToBranchRef(name), refs.readExistentHash("HEAD"));
    }
  },

  update_ref: function(refToUpdate, refToUpdateTo) {
    files.assertInRepo();

    if (!refs.isRef(refToUpdate)) {
      throw "fatal: Cannot lock the ref " + refToUpdate + ".";
    } else {
      var hash = refs.readExistentHash(refToUpdateTo);
      if (!objects.readExists(hash)) {
        throw "fatal: " + refToUpdateTo + ": not a valid SHA1";
      } else if (!(objects.type(objects.read(hash)) === "commit")) {
        throw "error: Trying to write non-commit object " + hash + " to branch " +
          refs.toLocalHead(refToUpdate) + "\n" +
          "fatal: Cannot update the ref " + refToUpdate;
      } else {
        refs.write(refs.toLocalHead(refToUpdate), hash);
      }
    }
  },

  checkout: function(ref) {
    files.assertInRepo();

    var finalRef = refs.isRef(ref) ? ref : refs.toFinalRef(ref);
    var hash = refs.toHash(finalRef);

    if (!objects.readExists(hash)) {
      throw "error: pathspec " + ref + " did not match any file(s) known to git."
    }
  },

  diff: function(ref1, ref2, opts) {
    files.assertInRepo();

    if (opts["name-only"] !== true) {
      throw "unsupported"; // for now
    }
  }
};

var head = {
  readCurrentBranchName: function() {
    if (this.read().match("refs")) {
      return this.read().match("refs/heads/(.+)")[1];
    }
  },

  read: function() {
    var content = files.read(nodePath.join(files.gimletDir(), "HEAD"));
    var refMatch = content.match("ref: (refs/heads/.+)");
    return refMatch ? refMatch[1] : content;
  },

  write: function(ref) {
    if (refs.isLocalHeadRef(ref)) {
      files.write(nodePath.join(files.gimletDir(), "HEAD"), "ref: " + ref + "\n");
    }
  }
};

var refs = {
  isLocalHeadRef: function(ref) {
    return ref.match("refs/heads/[A-Za-z-]+");
  },

  isRef: function(ref) {
    return ref === "HEAD" || this.isLocalHeadRef(ref);
  },

  toLocalHead: function(ref) {
    if (ref === "HEAD") {
      return head.read();
    } else if (this.isLocalHeadRef(ref)) {
      return ref;
    } else {
      return this.nameToBranchRef(ref);
    }
  },

  readExistentHash: function(ref) {
    if (objects.readExists(ref)) {
      return ref;
    } else if (this.readExists(this.toLocalHead(ref))) {
      return files.read(nodePath.join(files.gimletDir(), this.toLocalHead(ref)));
    } else if (this.readExists(this.nameToBranchRef(ref))) {
      return files.read(nodePath.join(files.gimletDir(), this.nameToBranchRef(ref)));
    }
  },

  nameToBranchRef: function(name) {
    return "refs/heads/" + name;
  },

  write: function(ref, content) {
    if (this.isLocalHeadRef(ref)) {
      files.write(nodePath.join(files.gimletDir(), ref), content);
    }
  },

  readLocalHeads: function() {
    return fs.readdirSync(nodePath.join(files.gimletDir(), "refs/heads/"));
  },

  readExists: function(ref) {
    return ref !== undefined &&
      this.isLocalHeadRef(ref) &&
      fs.existsSync(nodePath.join(files.gimletDir(), ref));
  }
};

var index = {
  readHasFile: function(path) {
    return this.strToObj(this.read())[path] !== undefined;
  },

  read: function() {
    return files.read(nodePath.join(files.gimletDir(), "index"));
  },

  writeFile: function(path) {
    var index = this.strToObj(this.read());
    index[path] = util.hash(files.read(nodePath.join(files.repoDir(), path)));
    gimletApi.hash_object(path, { w: true });
    this.write(index);
  },

  strToObj: function(content) { // CHUCK THIS WHEN REFACTOR DONE
    return util.lines(content)
      .reduce(function(index, blobStr) {
        var blobData = blobStr.split(/ /);
        index[blobData[0]] = blobData[1];
        return index;
      }, {});
  },

  write: function(index) {
    var indexStr = Object.keys(index)
        .map(function(path) { return path + " " + index[path]; })
        .join("\n")
        .concat("\n"); // trailing new line
    files.write(nodePath.join(files.gimletDir(), "index"), indexStr);
  },

  objToTree: function(obj) {
    var tree = {};
    Object.keys(obj).forEach(function(wholePath) {
      util.assocIn(tree, wholePath.split(nodePath.sep).concat(files.read(wholePath)));
    });

    return tree;
  }
};

var objects = {
  writeTree: function(tree) {
    var treeObject = Object.keys(tree).map(function(key) {
      if (util.isString(tree[key])) {
        return "blob " + util.hash(tree[key]) + " " + key;
      } else {
        return "tree " + objects.writeTree(tree[key]) + " " + key;
      }
    }).join("\n") + "\n";

    return this.write(treeObject);
  },

  commitContent: function(treeHash, message, parentHashes) {
    return "commit " + treeHash + "\n" +
      parentHashes.map(function(h) { return "parent " + h + "\n"; }).join("") +
      "Date:  " + new Date().toString() + "\n" +
      "\n" +
      "    " + message;
  },

  write: function(content) {
    var contentHash = util.hash(content);
    if (this.read(contentHash) === undefined) {
      var filePath = nodePath.join(files.gimletDir(), "objects", contentHash);
      files.write(filePath, content);
    }

    return contentHash;
  },

  readExists: function(objectHash) {
    return objectHash !== undefined &&
      fs.existsSync(nodePath.join(files.gimletDir(), "objects", objectHash));
  },

  read: function(objectHash) {
    var objectPath = nodePath.join(files.gimletDir(), "objects", objectHash);
    if (fs.existsSync(objectPath)) {
      return files.read(objectPath);
    }
  },

  type: function(content) {
    var firstToken = content.split(" ")[0];
    if (firstToken === "commit") {
      return "commit";
    } else if (firstToken === "tree" || firstToken === "blob") {
      return "tree";
    } else {
      return "blob";
    }
  },

  readTreeHash: function(content) {
    if (this.type(content) === "commit") {
      return content.split(/\s/)[1];
    } else if (this.type(content) === "tree") {
      return hash(content);
    }
  }
};

var files = {
  gimletDir: function(dir) {
    if (dir === undefined) { return this.gimletDir(process.cwd()); }

    if (fs.existsSync(dir)) {
      var potentialGimletDir = nodePath.join(dir, ".gimlet");
      if (fs.existsSync(potentialGimletDir)) {
        return potentialGimletDir;
      } else if (dir !== "/") {
        return this.gimletDir(nodePath.join(dir, ".."));
      }
    }
  },

  repoDir: function() {
    if (this.gimletDir() !== undefined) {
      return nodePath.join(this.gimletDir(), "..")
    }
  },

  inRepo: function(cwd) {
    return this.gimletDir(cwd) !== undefined;
  },

  assertInRepo: function() {
    if (!this.inRepo()) {
      throw "fatal: Not a gimlet repository (or any of the parent directories): .gimlet";
    }
  },

  pathFromRepoRoot: function(path) {
    return nodePath.relative(this.repoDir(), nodePath.join(process.cwd(), path));
  },

  createFilesFromTree: function(structure, prefix) {
    if (prefix === undefined) {
      return files.createFilesFromTree(structure, process.cwd());
    }

    Object.keys(structure).forEach(function(name) {
      var path = nodePath.join(prefix, name);
      if (util.isString(structure[name])) {
        files.write(path, structure[name]);
      } else {
        fs.mkdirSync(path, "777");
        files.createFilesFromTree(structure[name], path);
      }
    });
  },

  read: function(path) {
    return fs.readFileSync(path, "utf8");
  },

  write: function(path, content) {
    fs.writeFileSync(path, content);
  },

  lsRecursive: function(path) {
    if (!fs.existsSync(path)) {
      return [];
    } else if (fs.statSync(path).isFile()) {
      return [path];
    } else if (fs.statSync(path).isDirectory()) {
      var self = this;
      return fs.readdirSync(path).reduce(function(files, dirChild) {
        return files.concat(self.lsRecursive(nodePath.join(path, dirChild)));
      }, []);
    }
  }
};

var util = {
  isString: function(thing) {
    return typeof thing === "string";
  },

  hash: function(string) {
    var hashInt = 0;
    for (var i = 0; i < string.length; i++) {
      hashInt = hashInt * 31 + string.charCodeAt(i);
      hashInt = hashInt | 0;
    }

    return Math.abs(hashInt).toString(16);
  },

  assocIn: function(obj, arr) {
    if (arr.length === 2) {
      obj[arr[0]] = arr[1];
    } else if (arr.length > 2) {
      obj[arr[0]] = obj[arr[0]] || {};
      this.assocIn(obj[arr[0]], arr.slice(1));
    }
  },

  lines: function(str) {
    return str.split("\n").slice(0, -1); // last is empty
  }
};
<|endoftext|>


<commit_before>Package.describe({
  summary: "Serves a Meteor app over HTTP",
  version: '1.6.1'
});

Npm.depends({"basic-auth-connect": "1.0.0",
             "cookie-parser": "1.4.3",
             connect: "3.6.5",
             compression: "1.7.1",
             errorhandler: "1.5.0",
             parseurl: "1.3.2",
             send: "0.16.1",
             "stream-to-string": "1.1.0",
             "qs-middleware": "1.0.3",
             useragent: "2.2.1"});

Npm.strip({
  multiparty: ["test/"],
  useragent: ["test/"]
});

Cordova.depends({
  'cordova-plugin-whitelist': '1.3.2',
  'cordova-plugin-wkwebview-engine': '1.1.3',
  'cordova-plugin-meteor-webapp': '1.6.0'
});

Package.onUse(function (api) {
  api.use('ecmascript');
  api.use([
    'logging',
    'underscore',
    'routepolicy',
    'modern-browsers',
    'boilerplate-generator',
    'webapp-hashing'
  ], 'server');

  // At response serving time, webapp uses browser-policy if it is loaded. If
  // browser-policy is loaded, then it must be loaded after webapp
  // (browser-policy depends on webapp). So we don't explicitly depend in any
  // way on browser-policy here, but we use it when it is loaded, and it can be
  // loaded after webapp.
  api.mainModule('webapp_server.js', 'server');
  api.export('WebApp', 'server');
  api.export('WebAppInternals', 'server');
  api.export('main', 'server');

  api.mainModule('webapp_client.js', 'client');
  api.export('WebApp', 'client');

  api.mainModule('webapp_cordova.js', 'web.cordova');
});

Package.onTest(function (api) {
  api.use(['tinytest', 'ecmascript', 'webapp', 'http', 'underscore']);
  api.addFiles('webapp_tests.js', 'server');
  api.addFiles('webapp_client_tests.js', 'client');
  api.addFiles('socket_file_tests.js', 'server');

  api.addAssets('modern_test_asset.js', 'web.browser');
  api.addAssets('legacy_test_asset.js', 'legacy');
});
<commit_msg>Bump webapp package version to 1.6.2.<commit_after>Package.describe({
  summary: "Serves a Meteor app over HTTP",
  version: '1.6.2'
});

Npm.depends({"basic-auth-connect": "1.0.0",
             "cookie-parser": "1.4.3",
             connect: "3.6.5",
             compression: "1.7.1",
             errorhandler: "1.5.0",
             parseurl: "1.3.2",
             send: "0.16.1",
             "stream-to-string": "1.1.0",
             "qs-middleware": "1.0.3",
             useragent: "2.2.1"});

Npm.strip({
  multiparty: ["test/"],
  useragent: ["test/"]
});

Cordova.depends({
  'cordova-plugin-whitelist': '1.3.2',
  'cordova-plugin-wkwebview-engine': '1.1.3',
  'cordova-plugin-meteor-webapp': '1.6.0'
});

Package.onUse(function (api) {
  api.use('ecmascript');
  api.use([
    'logging',
    'underscore',
    'routepolicy',
    'modern-browsers',
    'boilerplate-generator',
    'webapp-hashing'
  ], 'server');

  // At response serving time, webapp uses browser-policy if it is loaded. If
  // browser-policy is loaded, then it must be loaded after webapp
  // (browser-policy depends on webapp). So we don't explicitly depend in any
  // way on browser-policy here, but we use it when it is loaded, and it can be
  // loaded after webapp.
  api.mainModule('webapp_server.js', 'server');
  api.export('WebApp', 'server');
  api.export('WebAppInternals', 'server');
  api.export('main', 'server');

  api.mainModule('webapp_client.js', 'client');
  api.export('WebApp', 'client');

  api.mainModule('webapp_cordova.js', 'web.cordova');
});

Package.onTest(function (api) {
  api.use(['tinytest', 'ecmascript', 'webapp', 'http', 'underscore']);
  api.addFiles('webapp_tests.js', 'server');
  api.addFiles('webapp_client_tests.js', 'client');
  api.addFiles('socket_file_tests.js', 'server');

  api.addAssets('modern_test_asset.js', 'web.browser');
  api.addAssets('legacy_test_asset.js', 'legacy');
});
<|endoftext|>


<commit_before>/* Welcome to Agile Toolkit JS framework. This is a main file which provides extensions on top of jQuery-UI */

/*
 @VERSION

 ATK4 Loader introduces a widget, which is used instead of default $('..').load() method.

 The new widget adds a number of useful features including:
  * verifying loaded content against session timeouts or errors
  * provides better ability to refresh content
  * handler support. What if loaded content contains unsaved data?
  */

/*
 In a complex use case you would be doing the following:

 1. For the element which you wish to be a selector, initialise this widget
 2. Set a current URL for that element during initialisation (base_url);
 3. Define what should happen if user clicks "cancel"
 4. Define what should happen if user finishes action successfuly inside
  4a. succesful action may pass arguments.

 $('#MyDiv')
   .atk4_loader({url: '/page.html'})
   .atk4_loader({})

 THE STRUCTURE OF THIS WIDGET MIGHT BE REWRITTEN

*/

$.widget('ui.atk4_loader', {

	/*
	 base_url will contain URL which will be used to refresh contents.
	*/
	base_url: undefined,

	/*
	 loading will be set to true, when contents of this widgets are being
	 loaded.
	*/
	loading: false,

	/*
	 when we are loading URLs, we will automaticaly pass arguments to cut stuff out
	*/
	cut_mode: 'page',
	cut: '1',

	/*
	 Helper contains some extra thingies
	*/
	helper: undefined,

	_init: function(){

		var self=this;
		/*
		this.options.debug=true;
		this.options.anchoring=true;
		*/

		this.element.addClass('atk4_loader');

		 if(this.options.cut_object){
			 this.cut_mode='object';
			 this.cut=this.options.cut_object;
		 }
		if(this.options.debug){
			var d=$('
');
			d.css({background:'#fe8',border: '1px solid black',position:'absolute',width:'100px',height:'50px'});
			$('').attr('title','Canceled close').attr('href','javascript: void(0)').text('X').css({float:'right'})
				.click(function(){ $(this).closest('div').next().css({border:'0px'});$(this).closest('div').remove(); }).appendTo(d);
			d.append(' ');
			$('').attr('title','Reload this region').attr('href','javascript: void(0)').text('Reload')
				.click(function(){ self.reload()}).appendTo(d);
			d.append(' ');
			$('').attr('title','Show URL').attr('href','javascript: void(0)').text('URL')
				.click(function(){ alert(self.base_url)}).appendTo(d);
			d.append(' ');
			$('').attr('title','Attempt to remove').attr('href','javascript: void(0)').text('Remove')
				.click(function(){ self.remove()}).appendTo(d);
			d.append(' ');
			$('').attr('title','Successful close').attr('href','javascript: void(0)').text('OK')
				.click(function(){ self.successClose()}).appendTo(d);
			d.append(' ');
			$('').attr('title','Canceled close').attr('href','javascript: void(0)').text('Cancel')
				.click(function(){ self.cancelClose()}).appendTo(d);

			d.insertBefore(self.element);
			d.draggable();
			self.helper=d;
			self.element.css({border:'1px dashed green'});
		}
	},
	destroy: function(){
		var self=this;
		/*
		if(self.parent_loader){
			self.parent_loader.unbind('atk4_loaderbeforeclose.'+self.element.attr('id'));
		}
		*/
		this.element.removeClass('atk4_loader');
		if(this.helper){
			this.helper.remove();
			this.helper=undefined;
		}
	},

    /*
	 This function fetches block of HTML from the server and puts it
	 inside specified element. This function is very similar to
	 $('..').load('http://'), however it improves evaluation
	 of scripts supplied inside loaded chunk.
	*/
    _loadHTML: function(el, url, callback, reload){
		var self=this;

		// We preserve support for selectors in URL,
		// as compatibility with jQuery, however avoid
		// using this. ATK4 will gladly render part of
		// the page for you. (cut_object, cut_region, cut_page)
		var selector, off = url.indexOf(" ");
		if ( off >= 0 ) {
			selector = url.slice(off, url.length);
			url = url.slice(0, off);
		}

		// Before actual loading start, we call a method, which might want
		// to display loading indicator somewhere on the page.
		if(self.loading){
			$.univ().loadingInProgress();
			return false;
		}
		var m;

		self.loading=true;
        $.atk4.get(url,null,function(res){
			/*
			if(res.substr(0,13)=='SESSION OVER:'){
				$.univ.dialogOK('Session over','Your session have been timed out',function(){ document.location='/'});
				return;
			}
			*/
            var scripts=[], source=res;

            while((s=source.indexOf("",e);

                scripts.push(source.substring(s2+1,e));
                source=source.substring(0,s)+source.substring(e2+1);
            }

			m=el;
			if(!(jQuery.browser.msie))m.hide();

			// Parse into Document
			var source=$('
').append(source);
			var n=source.children();

			if(n.length==1 && (reload || (n.attr('id') && n.attr('id')==el.attr('id')))){
				// Only one child have been returned to us. We also checked ID's and they match
				// with existing element. In this case we will be copying contents of
				// provided element
				n=n.contents();
			}else{
				// otherwise we will be copying all the elements (including text)
				if(reload){
					console.error('Cannot reload content: ',reload,n[0],n[1],n[2]);
				}
				n=source.contents();
			}
			el.empty();

			n.each(function(){
				$(this).remove().appendTo(el);
			});

            for(var i in scripts){
				try{
					window.region=el;
					if(eval.call)eval.call(window,scripts[i]);else
					// IE-pain
					with(window)eval(scripts[i]);
				}catch(e){
					console.error("JS:",e,scripts[i]);
				}
            };

			if(callback)$.atk4(callback,true);
			$.atk4(function(){
				m.show();
				m.find('form:first').find('input:visible,select:visible').eq(0).focus();
				//trigger('setfocus');
			});
		},function(){	// second callback, which is always called, when loading is completed
			self.loading=false;
		});
    },
	/*
	 This function is called before HTML loading is started. Redifine it
	 or bind to enhance it's functionality
	*/
	_loadingStart: function(){
		var self=this;
		if(false === self._trigger('loadingStart')){
			return false;
		}
	},

	reload: function(args){
		var url=this.base_url;
		if(args)this.base_url=$.atk4.addArgument(this.base_url,args);
		this.loadURL(this.base_url);
		this.base_url=url;
	},
	remove: function(){
		var self=this;
		self.helper && self.helper.css({background:'red'});
		//if(false === self._trigger('beforeclose')){
		if(self.element.find('.form_changed').length){
			if(!confirm('Changes on the form will be lost. Continue?'))return false;
		}
		return true;
	},
	successClose: function(){
		console.log('successClose');
	},
	cancelClose: function(){
		console.log('cancelClose');
	},
	setURL: function(url){
		var self=this;

		self.base_url=url;
	},
	loadURL: function(url,fn,strip_layer){
		/*
		 Function provided mainly for compatibility. It will load URL in the selector
		 and will set "fn" to fire off when loading is complete

		 Sometimes you would want to reload (atk4_reload) an element. This means,
		 you will receive the same element from AJAX. New element comes with the
		 same ID and sub-elements. What we have to do is copy children from the
		 received data into existing element.
		*/
		var self = this;

		if(self.loading){
			$.univ().loadingInProgress();
			return false;
		}

		//if(false === self._trigger('beforeclose')){
		if(self.element.find('.form_changed').length){
			if(!confirm('Changes on the form will be lost. Continue?'))return false;
		}
		// remove error messages
		$('#tiptip_holder').remove();
		self.base_url=url;

		url=$.atk4.addArgument(url,"cut_"+self.cut_mode+'='+self.cut);
		this._loadHTML(self.element,url,fn,strip_layer);
	}

});

$.extend($.ui.atk4_loader, {
	getter: 'remove'
});

$.fn.extend({
	atk4_load: function(url,fn){
		this.atk4_loader()
			.atk4_loader('loadURL',url,fn)
			;
	},
	atk4_reload: function(url,arg,fn){
        if(arg){
            $.each(arg,function(key,value){
                url=$.atk4.addArgument(url,key+'='+value);
            });
        }
		this.atk4_loader()
			.atk4_loader('loadURL',url,fn,true)
			;
	}
});
<commit_msg>a way to pass base_url to loader directly<commit_after>/* Welcome to Agile Toolkit JS framework. This is a main file which provides extensions on top of jQuery-UI */

/*
 @VERSION

 ATK4 Loader introduces a widget, which is used instead of default $('..').load() method.

 The new widget adds a number of useful features including:
  * verifying loaded content against session timeouts or errors
  * provides better ability to refresh content
  * handler support. What if loaded content contains unsaved data?
  */

/*
 In a complex use case you would be doing the following:

 1. For the element which you wish to be a selector, initialise this widget
 2. Set a current URL for that element during initialisation (base_url);
 3. Define what should happen if user clicks "cancel"
 4. Define what should happen if user finishes action successfuly inside
  4a. succesful action may pass arguments.

 $('#MyDiv')
   .atk4_loader({url: '/page.html'})
   .atk4_loader({})

 THE STRUCTURE OF THIS WIDGET MIGHT BE REWRITTEN

*/

$.widget('ui.atk4_loader', {

	/*
	 base_url will contain URL which will be used to refresh contents.
	*/
	base_url: undefined,

	/*
	 loading will be set to true, when contents of this widgets are being
	 loaded.
	*/
	loading: false,

	/*
	 when we are loading URLs, we will automaticaly pass arguments to cut stuff out
	*/
	cut_mode: 'page',
	cut: '1',

	/*
	 Helper contains some extra thingies
	*/
	helper: undefined,

	_init: function(){

		var self=this;
		/*
		this.options.debug=true;
		this.options.anchoring=true;
		*/

		this.element.addClass('atk4_loader');

		if(this.options.url){
			this.base_url=this.options.url;
		}
		 if(this.options.cut_object){
			 this.cut_mode='object';
			 this.cut=this.options.cut_object;
		 }
		if(this.options.debug){
			var d=$('
');
			d.css({background:'#fe8',border: '1px solid black',position:'absolute',width:'100px',height:'50px'});
			$('').attr('title','Canceled close').attr('href','javascript: void(0)').text('X').css({float:'right'})
				.click(function(){ $(this).closest('div').next().css({border:'0px'});$(this).closest('div').remove(); }).appendTo(d);
			d.append(' ');
			$('').attr('title','Reload this region').attr('href','javascript: void(0)').text('Reload')
				.click(function(){ self.reload()}).appendTo(d);
			d.append(' ');
			$('').attr('title','Show URL').attr('href','javascript: void(0)').text('URL')
				.click(function(){ alert(self.base_url)}).appendTo(d);
			d.append(' ');
			$('').attr('title','Attempt to remove').attr('href','javascript: void(0)').text('Remove')
				.click(function(){ self.remove()}).appendTo(d);
			d.append(' ');
			$('').attr('title','Successful close').attr('href','javascript: void(0)').text('OK')
				.click(function(){ self.successClose()}).appendTo(d);
			d.append(' ');
			$('').attr('title','Canceled close').attr('href','javascript: void(0)').text('Cancel')
				.click(function(){ self.cancelClose()}).appendTo(d);

			d.insertBefore(self.element);
			d.draggable();
			self.helper=d;
			self.element.css({border:'1px dashed green'});
		}
	},
	destroy: function(){
		var self=this;
		/*
		if(self.parent_loader){
			self.parent_loader.unbind('atk4_loaderbeforeclose.'+self.element.attr('id'));
		}
		*/
		this.element.removeClass('atk4_loader');
		if(this.helper){
			this.helper.remove();
			this.helper=undefined;
		}
	},

    /*
	 This function fetches block of HTML from the server and puts it
	 inside specified element. This function is very similar to
	 $('..').load('http://'), however it improves evaluation
	 of scripts supplied inside loaded chunk.
	*/
    _loadHTML: function(el, url, callback, reload){
		var self=this;

		// We preserve support for selectors in URL,
		// as compatibility with jQuery, however avoid
		// using this. ATK4 will gladly render part of
		// the page for you. (cut_object, cut_region, cut_page)
		var selector, off = url.indexOf(" ");
		if ( off >= 0 ) {
			selector = url.slice(off, url.length);
			url = url.slice(0, off);
		}

		// Before actual loading start, we call a method, which might want
		// to display loading indicator somewhere on the page.
		if(self.loading){
			$.univ().loadingInProgress();
			return false;
		}
		var m;

		self.loading=true;
        $.atk4.get(url,null,function(res){
			/*
			if(res.substr(0,13)=='SESSION OVER:'){
				$.univ.dialogOK('Session over','Your session have been timed out',function(){ document.location='/'});
				return;
			}
			*/
            var scripts=[], source=res;

            while((s=source.indexOf("",e);

                scripts.push(source.substring(s2+1,e));
                source=source.substring(0,s)+source.substring(e2+1);
            }

			m=el;
			if(!(jQuery.browser.msie))m.hide();

			// Parse into Document
			var source=$('
').append(source);
			var n=source.children();

			if(n.length==1 && (reload || (n.attr('id') && n.attr('id')==el.attr('id')))){
				// Only one child have been returned to us. We also checked ID's and they match
				// with existing element. In this case we will be copying contents of
				// provided element
				n=n.contents();
			}else{
				// otherwise we will be copying all the elements (including text)
				if(reload){
					console.error('Cannot reload content: ',reload,n[0],n[1],n[2]);
				}
				n=source.contents();
			}
			el.empty();

			n.each(function(){
				$(this).remove().appendTo(el);
			});

            for(var i in scripts){
				try{
					window.region=el;
					if(eval.call)eval.call(window,scripts[i]);else
					// IE-pain
					with(window)eval(scripts[i]);
				}catch(e){
					console.error("JS:",e,scripts[i]);
				}
            };

			if(callback)$.atk4(callback,true);
			$.atk4(function(){
				m.show();
				m.find('form:first').find('input:visible,select:visible').eq(0).focus();
				//trigger('setfocus');
			});
		},function(){	// second callback, which is always called, when loading is completed
			self.loading=false;
		});
    },
	/*
	 This function is called before HTML loading is started. Redifine it
	 or bind to enhance it's functionality
	*/
	_loadingStart: function(){
		var self=this;
		if(false === self._trigger('loadingStart')){
			return false;
		}
	},

	reload: function(args){
		var url=this.base_url;
		if(args)this.base_url=$.atk4.addArgument(this.base_url,args);
		this.loadURL(this.base_url);
		this.base_url=url;
	},
	remove: function(){
		var self=this;
		self.helper && self.helper.css({background:'red'});
		//if(false === self._trigger('beforeclose')){
		if(self.element.find('.form_changed').length){
			if(!confirm('Changes on the form will be lost. Continue?'))return false;
		}
		return true;
	},
	successClose: function(){
		console.log('successClose');
	},
	cancelClose: function(){
		console.log('cancelClose');
	},
	setURL: function(url){
		var self=this;

		self.base_url=url;
	},
	loadURL: function(url,fn,strip_layer){
		/*
		 Function provided mainly for compatibility. It will load URL in the selector
		 and will set "fn" to fire off when loading is complete

		 Sometimes you would want to reload (atk4_reload) an element. This means,
		 you will receive the same element from AJAX. New element comes with the
		 same ID and sub-elements. What we have to do is copy children from the
		 received data into existing element.
		*/
		var self = this;

		if(self.loading){
			$.univ().loadingInProgress();
			return false;
		}

		//if(false === self._trigger('beforeclose')){
		if(self.element.find('.form_changed').length){
			if(!confirm('Changes on the form will be lost. Continue?'))return false;
		}
		// remove error messages
		$('#tiptip_holder').remove();
		self.base_url=url;

		url=$.atk4.addArgument(url,"cut_"+self.cut_mode+'='+self.cut);
		this._loadHTML(self.element,url,fn,strip_layer);
	}

});

$.extend($.ui.atk4_loader, {
	getter: 'remove'
});

$.fn.extend({
	atk4_load: function(url,fn){
		this.atk4_loader()
			.atk4_loader('loadURL',url,fn)
			;
	},
	atk4_reload: function(url,arg,fn){
        if(arg){
            $.each(arg,function(key,value){
                url=$.atk4.addArgument(url,key+'='+value);
            });
        }
		this.atk4_loader()
			.atk4_loader('loadURL',url,fn,true)
			;
	}
});
<|endoftext|>