<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 => {
        if (!this.editor || !this.editor.setOptions) return;
        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, ctx) {
      return lively.boundEval(str, ctx)
    }

    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, this.getDoitContext()) }
        catch(e) {
            console.error(e)
            if (printResult) {
                window.LastError = e
                this.printResult("" +e)
            }
            return
        }
        if (printResult) {
            // alaways wait on promises.. when interactively working...
            if (result && result.then) {
              // we will definitly return a promise on which we can wait here
              result
                .then( result => this.printResult("" +result))
                .catch( error => {
                  console.error(error);
                  // window.LastError = error;
                  this.printResult("Error in Promise: \n" +error)
                })
            } 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);

        this.isSpellChecking = true;
    }

    disableSpellcheck () {
        this.isSpellChecking = false;
        this.clearSpellCheckMarkers()
        // how to remove
    }

    clearSpellCheckMarkers () {
        var session = this.editor.getSession();
        for (var i in this.markers_present) {
          session.removeMarker(this.markers_present[i]);
        }
        this.markers_present = [];

        // clear the gutter
        var lines = session.getDocument().getAllLines();
    	  for (var i in lines) {
    	  	// Clear the gutter.
    	    session.removeGutterDecoration(i, "misspelled");
    	  }

    }

    // Spell check the Ace editor contents.
    spellCheck() {
        if (!this.isSpellChecking) return

        // 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();

        this.clearSpellCheckMarkers()

        try {
      	  var Range = ace.require('ace/range').Range
      	  var lines = session.getDocument().getAllLines();
      	  for (var i in lines) {
      	    // 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;
      	}
      }
}

<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 && 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 => {
        if (!this.editor || !this.editor.setOptions) return;
        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, ctx) {
      return lively.boundEval(str, ctx)
    }

    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, this.getDoitContext()) }
        catch(e) {
            console.error(e)
            if (printResult) {
                window.LastError = e
                this.printResult("" +e)
            }
            return
        }
        if (printResult) {
            // alaways wait on promises.. when interactively working...
            if (result && result.then) {
              // we will definitly return a promise on which we can wait here
              result
                .then( result => this.printResult("" +result))
                .catch( error => {
                  console.error(error);
                  // window.LastError = error;
                  this.printResult("Error in Promise: \n" +error)
                })
            } 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);

        this.isSpellChecking = true;
    }

    disableSpellcheck () {
        this.isSpellChecking = false;
        this.clearSpellCheckMarkers()
        // how to remove
    }

    clearSpellCheckMarkers () {
        var session = this.editor.getSession();
        for (var i in this.markers_present) {
          session.removeMarker(this.markers_present[i]);
        }
        this.markers_present = [];

        // clear the gutter
        var lines = session.getDocument().getAllLines();
    	  for (var i in lines) {
    	  	// Clear the gutter.
    	    session.removeGutterDecoration(i, "misspelled");
    	  }

    }

    // Spell check the Ace editor contents.
    spellCheck() {
        if (!this.isSpellChecking) return

        // 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();

        this.clearSpellCheckMarkers()

        try {
      	  var Range = ace.require('ace/range').Range
      	  var lines = session.getDocument().getAllLines();
      	  for (var i in lines) {
      	    // 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>//
// See https://github.com/jenkinsci/js-builder
//
var builder = require('@jenkins-cd/js-builder');

// Explicitly setting the src paths in order to allow the rebundle task to
// watch for changes in the JDL (js, css, icons etc).
// See https://github.com/jenkinsci/js-builder#setting-src-and-test-spec-paths
builder.src(['src/main/js', 'src/main/less', 'node_modules/@jenkins-cd/design-language/dist']);

//
// Create the main "App" bundle.
// generateNoImportsBundle makes it easier to test with zombie.
//
builder.bundle('src/main/js/blueocean.js')
    // .withExternalModuleMapping('jquery-detached', 'jquery-detached:jquery2')
    .inDir('target/classes/io/jenkins/blueocean')
    .less('src/main/less/blueocean.less')
    .generateNoImportsBundle();
<commit_msg>Update gulpfile.js<commit_after>//
// See https://github.com/jenkinsci/js-builder
//
var builder = require('@jenkins-cd/js-builder');

// Explicitly setting the src paths in order to allow the rebundle task to
// watch for changes in the JDL (js, css, icons etc).
// See https://github.com/jenkinsci/js-builder#setting-src-and-test-spec-paths
builder.src(['src/main/js', 'src/main/less', 'node_modules/@jenkins-cd/design-language/dist']);

//
// Create the main "App" bundle.
// generateNoImportsBundle makes it easier to test with zombie.
//
builder.bundle('src/main/js/blueocean.js')
    .inDir('target/classes/io/jenkins/blueocean')
    .less('src/main/less/blueocean.less')
    .generateNoImportsBundle();
<|endoftext|>


<commit_before><commit_msg>pull updates from reveal.js submodule<commit_after><|endoftext|>


<commit_before>var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementById('particles-js')[0],
    width = w.innerWidth || e.clientWidth || g.clientWidth,
    height = w.innerHeight|| e.clientHeight || g.clientHeight;

var svg = d3.select("#particles-js").append("svg").attr("width",width)
            .attr("height",height);

var colorScale = d3.scaleLinear().domain([0,1.5]).range(["black","red"]);

var nodeData = [];

var hexbin = d3.hexbin()
    .size([width, height])
    .radius(26);

var k, numcircs=hexbin.centers().length;
for (k = 0; k < numcircs; k++) {
    nodeData.push( { 'x': hexbin.centers()[k][0],
      'y': hexbin.centers()[k][1],
      'r': Math.random() * 14 + 14});
}

var nodePadding = 0.02

var collisionForce = d3.forceCollide().radius(function(d){ return d.r + nodePadding; }).strength(0.8).iterations(50);

var simulation = d3.forceSimulation(nodeData).alphaDecay(0.01).force("collisionForce",collisionForce);

 var node = svg.selectAll("circle").data(nodeData)
            .enter().append("circle")
            .attr("r",function(d){ return d.r;}).attr("cx",function(d){ return d.x;}).attr("cy",function(d){ return d.y;})
            .attr("fill","black").attr("opacity",0.5)
            .call(d3.drag()
            .on("start",dragstarted)
            .on("drag",dragged)
            .on("end",dragended));

 function dragstarted(d)
 {
    collisionForce.strength(1.9);
    simulation.restart();
    simulation.alpha(0.7);
    d.fx = d.x;
    d.fy = d.y;
 }

 function dragged(d)
 {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
 }

 function dragended(d)
 {
    d.fx = null;
    d.fy = null;
    simulation.alphaTarget(0.1);
 }

 function ticker(){
     node.attr("cx", function(d){ return d.x;})
         .attr("cy", function(d){ return d.y;})
         .style("fill", function(d) {
           return colorScale((d.vx*d.vx)+(d.vy*d.vy));
         })
 }

 simulation.on("tick",ticker);
<commit_msg>orientation change<commit_after>var w = window,
    d = document,
    e = d.documentElement,
    g = d.getElementById('particles-js')[0],
    width = w.innerWidth || e.clientWidth || g.clientWidth,
    height = w.innerHeight|| e.clientHeight || g.clientHeight;

var svg = d3.select("#particles-js").append("svg").attr("width",width)
            .attr("height",height);

var colorScale = d3.scaleLinear().domain([0,1.5]).range(["black","red"]);

var nodeData = [];

var hexbin = d3.hexbin()
    .size([width, height])
    .radius(26);

var k, numcircs=hexbin.centers().length;
for (k = 0; k < numcircs; k++) {
    nodeData.push( { 'x': hexbin.centers()[k][0],
      'y': hexbin.centers()[k][1],
      'r': Math.random() * 14 + 14});
}

var nodePadding = 0.02

var collisionForce = d3.forceCollide().radius(function(d){ return d.r + nodePadding; }).strength(0.8).iterations(50);

var simulation = d3.forceSimulation(nodeData).alphaDecay(0.01).force("collisionForce",collisionForce);

 var node = svg.selectAll("circle").data(nodeData)
            .enter().append("circle")
            .attr("r",function(d){ return d.r;}).attr("cx",function(d){ return d.x;}).attr("cy",function(d){ return d.y;})
            .attr("fill","black").attr("opacity",0.5)
            .call(d3.drag()
            .on("start",dragstarted)
            .on("drag",dragged)
            .on("end",dragended));

 function dragstarted(d)
 {
    collisionForce.strength(1.9);
    simulation.restart();
    simulation.alpha(0.7);
    d.fx = d.x;
    d.fy = d.y;
 }

 function dragged(d)
 {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
 }

 function dragended(d)
 {
    d.fx = null;
    d.fy = null;
    simulation.alphaTarget(0.1);
 }

 function ticker(){
     node.attr("cx", function(d){ return d.x;})
         .attr("cy", function(d){ return d.y;})
         .style("fill", function(d) {
           return colorScale((d.vx*d.vx)+(d.vy*d.vy));
         })
 }

 simulation.on("tick",ticker);

 w.addEventListener('orientationchange', function () {
   var originalBodyStyle = getComputedStyle(document.body).getPropertyValue('display');
   document.body.style.display='none';
   setTimeout(function () {
     document.body.style.display = originalBodyStyle;
   }, 10);
 });
<|endoftext|>


<commit_before>/* $This file is distributed under the terms of the license in /doc/license.txt$ */

/*
 * ----------------------------------------------------------------------------
 * proxyItemsPanel
 * ----------------------------------------------------------------------------
 * Display an AJAX-enabled list of proxy-related items (either proxies or
 * profiles).
 *
 * The list may start out with a population of items. items may be added by
 * selecting them in the auto-complete box. Items may be removed by clicking
 * the "remove" link next to that item.
 *
 * A hidden field will hold the URI for each item, so when the form is submitted,
 * the controller can determine the list of items.
 * ----------------------------------------------------------------------------
 * You provide:
 *   p -- the DOM element that contains the template and the data.
 *         It also contains the autocomplete field.
 * ----------------------------------------------------------------------------
 */
function proxyItemsPanel(panel, contextInfo)  {
	var self = this;

	this.itemData = [];

	var dataContainerElement = $("[name='proxyData']", panel).first();
	var autoCompleteField = $("input[name='proxySelectorAC']", panel).first();
	var searchStatusField = $("span[name='proxySelectorSearchStatus']", panel).first();
	var excludedUris =  [];
	$("[name='excludeUri']", panel).each(function(index) {
		excludedUris.push($(this).text());
	});

	var parseTemplate = function(dataContainer) {
		var templateDiv = $("div[name='template']", dataContainer)
		var templateHtml = templateDiv.html();
		templateDiv.remove();
		return templateHtml;
	};
	this.templateHtml = parseTemplate(dataContainerElement);

	this.displayItemData = function() {
		$(".proxyInfoElement", dataContainerElement).remove();

		for (i = 0; i < self.itemData.length; i++) {
			self.itemData[i].element().appendTo(dataContainerElement);
		}
	}

	var getItemData = function() {
		return self.itemData;
	}

	this.removeItem = function(info) {
		var idx = self.itemData.indexOf(info);
		if (idx != -1) {
			self.itemData.splice(idx, 1);
		}
		self.displayItemData();
	}

	this.addItemData = function(selection) {
		var info = new itemElement(self.templateHtml, selection.uri, selection.label,
				selection.classLabel, selection.imageUrl, self.removeItem);
        self.itemData.unshift(info);
        self.displayItemData();
        self.getAdditionalData(self, info, selection.externalAuthId)
	}

	this.getAdditionalData = function(parent, info, externalAuthId) {
		data = info
	    $.ajax({
	        url: contextInfo.ajaxUrl,
	        dataType: 'json',
	        data: {
	        	action: contextInfo.moreInfoAction,
	        	uri: info.uri
	        },
	        complete: function(xhr, status) {
	            var results = $.parseJSON(xhr.responseText);
	            if (results.length > 0) {
	                if ("classLabel" in results[0]) {
	                    info.classLabel = results[0].classLabel;
	                }
	                if ("imageUrl" in results[0]) {
	                	info.imageUrl = results[0].imageUrl;
	                }
	                self.displayItemData();
	            }
	        }
	    });
	}

	var parseOriginalData = function() {
		var dataDivs = $("div[name='data']", dataContainerElement)
		var data = [];
		for (i = 0; i < dataDivs.length; i++) {
			var dd = dataDivs[i];
			var uri = $("p[name='uri']", dd).text();
			var label = $("p[name='label']", dd).text();
			var classLabel = $("p[name='classLabel']", dd).text();
			var imageUrl = $("p[name='imageUrl']", dd).text();
			data.push(new itemElement(self.templateHtml, uri, label, classLabel, imageUrl, self.removeItem));
		}
		return data;
	}
	this.itemData = parseOriginalData();

	var setupAutoCompleteFields = function() {
		var parms = {
		    url: contextInfo.ajaxUrl,
		    action: contextInfo.basicInfoAction
		}
		var updateStatus = new statusFieldUpdater(searchStatusField, 3).setText;
		var autocompleteInfo = new proxyAutocomplete(parms, excludedUris, getItemData, self.addItemData, updateStatus)
	    autoCompleteField.autocomplete(autocompleteInfo);
	}
	setupAutoCompleteFields();

	self.displayItemData();
}

function statusFieldUpdater(element, minLength) {
	var emptyText = element.text();
	var moreCharsText = element.attr('moreCharsText');
	var noMatchText = element.attr('noMatchText');

	this.setText = function(searchTermLength, numberOfResults) {
		if (numberOfResults > 0) {
			element.text('');
		} else if (searchTermLength == 0) {
			element.text(emptyText);
		} else if (searchTermLength < minLength) {
			element.text(moreCharsText);
		} else {
			element.text(noMatchText);
		}
	}
}

/*
 * Execute this when the page loads.
 */
$(document).ready(function() {
	var disableFormInUnsupportedBrowsers = function() {
		var disableWrapper = $('#ie67DisableWrapper');

		// Check for unsupported browsers only if the element exists on the page
		if (disableWrapper.length) {
			if (vitro.browserUtils.isIELessThan8()) {
				disableWrapper.show();
				$('.noIE67').hide();
				return true;
			}
		}
		return false;
	};

	/* If we don't support this form in this browser, just stop here. */
	if (disableFormInUnsupportedBrowsers()) {
		return;
	}

	$("section[name='proxyProfilesPanel']").each(function(i) {
		var context = {
			baseUrl: proxyContextInfo.baseUrl,
			ajaxUrl: proxyContextInfo.ajaxUrl,
			basicInfoAction: "getAvailableProfiles",
			moreInfoAction: "moreProfileInfo"
		}
		this["proxyItemsPanel"] = new proxyItemsPanel(this, context);
	});

	$("section[name='proxyProxiesPanel']").each(function(i) {
		var context = {
			baseUrl: proxyContextInfo.baseUrl,
			ajaxUrl: proxyContextInfo.ajaxUrl,
			basicInfoAction: "getAvailableProxies",
			moreInfoAction: "moreProxyInfo"
		}
		this["proxyItemsPanel"] = new proxyItemsPanel(this, context);
	});
});
<commit_msg>NIHVIVO-3274 Bug fix - Internet Explorer before 9 does not support Array.indexOf<commit_after>/* $This file is distributed under the terms of the license in /doc/license.txt$ */

/*
 * ----------------------------------------------------------------------------
 * proxyItemsPanel
 * ----------------------------------------------------------------------------
 * Display an AJAX-enabled list of proxy-related items (either proxies or
 * profiles).
 *
 * The list may start out with a population of items. items may be added by
 * selecting them in the auto-complete box. Items may be removed by clicking
 * the "remove" link next to that item.
 *
 * A hidden field will hold the URI for each item, so when the form is submitted,
 * the controller can determine the list of items.
 * ----------------------------------------------------------------------------
 * You provide:
 *   p -- the DOM element that contains the template and the data.
 *         It also contains the autocomplete field, with a status element and
 *         perhaps 1 or more excluded URIs
 * ----------------------------------------------------------------------------
 */
function proxyItemsPanel(panel, contextInfo)  {
	var self = this;

	this.itemData = [];

	var dataContainerElement = $("[name='proxyData']", panel).first();
	var autoCompleteField = $("input[name='proxySelectorAC']", panel).first();
	var searchStatusField = $("span[name='proxySelectorSearchStatus']", panel).first();
	var excludedUris =  [];
	$("[name='excludeUri']", panel).each(function(index) {
		excludedUris.push($(this).text());
	});

	var parseTemplate = function(dataContainer) {
		var templateDiv = $("div[name='template']", dataContainer)
		var templateHtml = templateDiv.html();
		templateDiv.remove();
		return templateHtml;
	};
	this.templateHtml = parseTemplate(dataContainerElement);

	this.displayItemData = function() {
		$(".proxyInfoElement", dataContainerElement).remove();

		for (i = 0; i < self.itemData.length; i++) {
			self.itemData[i].element().appendTo(dataContainerElement);
		}
	}

	var getItemData = function() {
		return self.itemData;
	}

	this.removeItem = function(info) {
		var i;
		for (i = 0; i < self.itemData.length; i++) {
			if (self.itemData[i] === info) {
				self.itemData.splice(i, 1);
				break;
			}
		}
		self.displayItemData();
	}

	this.addItemData = function(selection) {
		var info = new itemElement(self.templateHtml, selection.uri, selection.label,
				selection.classLabel, selection.imageUrl, self.removeItem);
        self.itemData.unshift(info);
        self.displayItemData();
        self.getAdditionalData(self, info, selection.externalAuthId)
	}

	this.getAdditionalData = function(parent, info, externalAuthId) {
		data = info
	    $.ajax({
	        url: contextInfo.ajaxUrl,
	        dataType: 'json',
	        data: {
	        	action: contextInfo.moreInfoAction,
	        	uri: info.uri
	        },
	        complete: function(xhr, status) {
	            var results = $.parseJSON(xhr.responseText);
	            if (results.length > 0) {
	                if ("classLabel" in results[0]) {
	                    info.classLabel = results[0].classLabel;
	                }
	                if ("imageUrl" in results[0]) {
	                	info.imageUrl = results[0].imageUrl;
	                }
	                self.displayItemData();
	            }
	        }
	    });
	}

	var parseOriginalData = function() {
		var dataDivs = $("div[name='data']", dataContainerElement)
		var data = [];
		for (i = 0; i < dataDivs.length; i++) {
			var dd = dataDivs[i];
			var uri = $("p[name='uri']", dd).text();
			var label = $("p[name='label']", dd).text();
			var classLabel = $("p[name='classLabel']", dd).text();
			var imageUrl = $("p[name='imageUrl']", dd).text();
			data.push(new itemElement(self.templateHtml, uri, label, classLabel, imageUrl, self.removeItem));
		}
		return data;
	}
	this.itemData = parseOriginalData();

	var setupAutoCompleteFields = function() {
		var parms = {
		    url: contextInfo.ajaxUrl,
		    action: contextInfo.basicInfoAction
		}
		var updateStatus = new statusFieldUpdater(searchStatusField, 3).setText;
		var autocompleteInfo = new proxyAutocomplete(parms, excludedUris, getItemData, self.addItemData, updateStatus)
	    autoCompleteField.autocomplete(autocompleteInfo);
	}
	setupAutoCompleteFields();

	self.displayItemData();
}

function statusFieldUpdater(element, minLength) {
	var emptyText = element.text();
	var moreCharsText = element.attr('moreCharsText');
	var noMatchText = element.attr('noMatchText');

	this.setText = function(searchTermLength, numberOfResults) {
		if (numberOfResults > 0) {
			element.text('');
		} else if (searchTermLength == 0) {
			element.text(emptyText);
		} else if (searchTermLength < minLength) {
			element.text(moreCharsText);
		} else {
			element.text(noMatchText);
		}
	}
}

/*
 * Execute this when the page loads.
 */
$(document).ready(function() {
	var disableFormInUnsupportedBrowsers = function() {
		var disableWrapper = $('#ie67DisableWrapper');

		// Check for unsupported browsers only if the element exists on the page
		if (disableWrapper.length) {
			if (vitro.browserUtils.isIELessThan8()) {
				disableWrapper.show();
				$('.noIE67').hide();
				return true;
			}
		}
		return false;
	};

	/* If we don't support this form in this browser, just stop here. */
	if (disableFormInUnsupportedBrowsers()) {
		return;
	}

	$("section[name='proxyProfilesPanel']").each(function(i) {
		var context = {
			baseUrl: proxyContextInfo.baseUrl,
			ajaxUrl: proxyContextInfo.ajaxUrl,
			basicInfoAction: "getAvailableProfiles",
			moreInfoAction: "moreProfileInfo"
		}
		this["proxyItemsPanel"] = new proxyItemsPanel(this, context);
	});

	$("section[name='proxyProxiesPanel']").each(function(i) {
		var context = {
			baseUrl: proxyContextInfo.baseUrl,
			ajaxUrl: proxyContextInfo.ajaxUrl,
			basicInfoAction: "getAvailableProxies",
			moreInfoAction: "moreProxyInfo"
		}
		this["proxyItemsPanel"] = new proxyItemsPanel(this, context);
	});
});
<|endoftext|>


<commit_before>$(document).ready(function() {

  var turnCounter = 0

  $('.space').click(function(){

    var row = $(this).attr('class').split(" ")[1]
    var column = $(this).attr('class').split(" ")[2]

    if ($(this).hasClass('empty')) {
      if ( turnCounter % 2 === 0 ) {
        $(this).css('background-color', 'red')
        $(this).removeClass('empty')
        $(this).addClass('filled')
        turnCounter += 1
      } else {
        $(this).css('background-color', 'black')
        $(this).removeClass('empty')
        $(this).addClass('filled')
        turnCounter += 1
      }
    }

  })
});

<commit_msg>Fill spaces from bottom<commit_after>$(document).ready(function() {

  var turnCounter = 0

  $('.space').click(function(){

    var row = "." + $(this).attr('class').split(" ")[1]
    var column = "." + $(this).attr('class').split(" ")[2]

    for ( var i = 1; i < 8; i++) {
      if ($(column + ".row" + i).hasClass('empty')) {
        bottomRow = ".row" + i
        break
      }
    }
    var $bottomColumnSpace = $(column + bottomRow)

    if ($(this).hasClass('empty')) {
      if ( turnCounter % 2 === 0 ) {
        $bottomColumnSpace.css('background-color', 'red')
        $bottomColumnSpace.removeClass('empty')
        $bottomColumnSpace.addClass('filled')
        turnCounter += 1
      } else {
        $bottomColumnSpace.css('background-color', 'black')
        $bottomColumnSpace.removeClass('empty')
        $bottomColumnSpace.addClass('filled')
        turnCounter += 1
      }
    }

  })
});

<|endoftext|>


<commit_before>function load_roast_graph(roast) {
    $.ajax({
        type: 'get',
        url: '/roast/data/' + roast.id_urlescaped + '.json',
        dataType: 'json',
        success: function(data) {
            var ctx = document.getElementById("roastChart").getContext('2d');
            var roastChart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: data['time'],
                    datasets: [
                        {
                            label: 'Cracks',
                            data: data['data']['crack'],
                            fill: false,
                            showLine: false,
                            pointStyle: 'star',
                            pointRadius: 10,
                            yAxisID: 'tempAxis',
                            pointBorderWidth: 5,
                            borderColor: '#466365',
                            backgroundColor: '#466365'
                        },
                        {
                            label: 'Heat',
                            data: data['data']['heat'],
                            fill: false,
                            yAxisID: 'percentAxis',
                            borderColor: '#F4C95D',
                            backgroundColor: '#F4C95D'
                        },
                        {
                            label: 'Temp.',
                            data: data['data']['temp'],
                            fill: false,
                            yAxisID: 'tempAxis',
                            borderColor: '#DD7230',
                            backgroundColor: '#DD7230'
                        }]
                },
                options: {
                    tooltips: {
                        enabled: false
                    },
                    scales: {
                        xAxes: [{
                            scaleLabel: {display:true, labelString: 'Time'},
                            type: 'time',
                            time: {
                                unit: 'minute',
                                displayFormats: {
                                    minute: 'mm:ss'
                                }
                            },
                        }],
                        yAxes: [
                            {
                                id: 'tempAxis',
                                scaleLabel: {
                                    display: true,
                                    labelString: '°C'
                                },
                                type: 'linear',
                                position: 'left'
                            },
                            {
                                id: 'percentAxis',
                                scaleLabel: {
                                    display: true,
                                    labelString: '%'
                                },
                                type: 'linear',
                                position: 'right'
                            }
                        ]
                    }
                }
            });
        }
    })
}

$(document).ready(function() {
    $("#roast_desc_form").submit(function(event) {
        $("#roast_desc_form button[type=submit]").prop("disabled", true);
        $("#submit_spinner").show();

        var newRoast = {
            'coffee': $('#inputCoffee').val(),
            'roast_degree' : $('#inputRoastDegree').val(),
            'pre_roast_weight' : $('#inputRoastPreWeight').val(),
            'post_roast_weight' : $('#inputRoastPostWeight').val(),
            'rating' : $('#inputRoastRating').val(),
            'comment' : $('#inputRoastComment').val()
        };
        $.ajax({
            type: 'POST',
            url: '/roast/' + roast.id_urlescaped,
            data: newRoast,
            encode: true
        }).success(function() {
            $("#roast_desc_form button[type=submit]").prop("disabled", false);
            $("#submit_spinner").hide();
        });
        event.preventDefault();
    });

    $("#delete_btn").click(function(event) {
        if(confirm("Are you sure?")) {
            $("#delete_btn").prop("disabled", true);
            $("#delete_spinner").show();
            $.ajax({
                type: 'DELETE',
                url: '/roast/' + roast.id_urlescaped,
                encode: true
            }).success(function() {
                $("#delete_btn").prop("disabled", false);
                $("#delete_spinner").hide();
                window.location.replace("/");
            });
        }
    });
});
<commit_msg>Represent integer values as integers<commit_after>function load_roast_graph(roast) {
    $.ajax({
        type: 'get',
        url: '/roast/data/' + roast.id_urlescaped + '.json',
        dataType: 'json',
        success: function(data) {
            var ctx = document.getElementById("roastChart").getContext('2d');
            var roastChart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: data['time'],
                    datasets: [
                        {
                            label: 'Cracks',
                            data: data['data']['crack'],
                            fill: false,
                            showLine: false,
                            pointStyle: 'star',
                            pointRadius: 10,
                            yAxisID: 'tempAxis',
                            pointBorderWidth: 5,
                            borderColor: '#466365',
                            backgroundColor: '#466365'
                        },
                        {
                            label: 'Heat',
                            data: data['data']['heat'],
                            fill: false,
                            yAxisID: 'percentAxis',
                            borderColor: '#F4C95D',
                            backgroundColor: '#F4C95D'
                        },
                        {
                            label: 'Temp.',
                            data: data['data']['temp'],
                            fill: false,
                            yAxisID: 'tempAxis',
                            borderColor: '#DD7230',
                            backgroundColor: '#DD7230'
                        }]
                },
                options: {
                    tooltips: {
                        enabled: false
                    },
                    scales: {
                        xAxes: [{
                            scaleLabel: {display:true, labelString: 'Time'},
                            type: 'time',
                            time: {
                                unit: 'minute',
                                displayFormats: {
                                    minute: 'mm:ss'
                                }
                            },
                        }],
                        yAxes: [
                            {
                                id: 'tempAxis',
                                scaleLabel: {
                                    display: true,
                                    labelString: '°C'
                                },
                                type: 'linear',
                                position: 'left'
                            },
                            {
                                id: 'percentAxis',
                                scaleLabel: {
                                    display: true,
                                    labelString: '%'
                                },
                                type: 'linear',
                                position: 'right'
                            }
                        ]
                    }
                }
            });
        }
    })
}

$(document).ready(function() {
    $("#roast_desc_form").submit(function(event) {
        $("#roast_desc_form button[type=submit]").prop("disabled", true);
        $("#submit_spinner").show();

        var newRoast = {
            'coffee': $('#inputCoffee').val(),
            'roast_degree' : $('#inputRoastDegree').val(),
            'pre_roast_weight' : parseInt($('#inputRoastPreWeight').val()),
            'post_roast_weight' : parseInt($('#inputRoastPostWeight').val()),
            'rating' : parseInt($('#inputRoastRating').val()),
            'comment' : $('#inputRoastComment').val()
        };
        $.ajax({
            type: 'POST',
            url: '/roast/' + roast.id_urlescaped,
            data: newRoast,
            encode: true
        }).success(function() {
            $("#roast_desc_form button[type=submit]").prop("disabled", false);
            $("#submit_spinner").hide();
        });
        event.preventDefault();
    });

    $("#delete_btn").click(function(event) {
        if(confirm("Are you sure?")) {
            $("#delete_btn").prop("disabled", true);
            $("#delete_spinner").show();
            $.ajax({
                type: 'DELETE',
                url: '/roast/' + roast.id_urlescaped,
                encode: true
            }).success(function() {
                $("#delete_btn").prop("disabled", false);
                $("#delete_spinner").hide();
                window.location.replace("/");
            });
        }
    });
});
<|endoftext|>


<commit_before>require("dotenv").config({
    path: `.env.${process.env.NODE_ENV}`
});

module.exports = {
    siteMetadata: {
        siteUrl: "https://mondora.com",
        title: ":mondora - Building software, creating benefit",
        description:
            "Software and advisory company specialized in custom cloud solutions. Our aim is to create benefit for all stakeholders by designing and building software solutions that maximise positive impact.",
        author: "mondora-team"
    },
    plugins: [
        "gatsby-plugin-transition-link",
        "gatsby-plugin-react-helmet",
        "gatsby-plugin-styled-components",
        "gatsby-transformer-sharp",
        "gatsby-plugin-sharp",
        {
            resolve: "gatsby-source-rss-feed",
            options: {
                url: "https://bcalmbcorp.com/feed/",
                name: "BcalmBcorp"
            }
        },
        {
            resolve: "gatsby-plugin-manifest",
            options: {
                icon: "src/images/logo/square.png",
                name: ":mondora",
                short_name: ":m",
                start_url: "/",
                background_color: "#f2f2f2",
                theme_color: "#ffda01",
                display: "minimal-ui"
            }
        },
        {
            resolve: "gatsby-source-filesystem",
            options: {
                name: "images",
                path: `${__dirname}/src/images`
            }
        },
        {
            resolve: "gatsby-source-instagram",
            options: {
                username: "mondoracom"
            }
        },
        {
            resolve: "gatsby-plugin-sitemap",
            options: {
                exclude: ["/meet-the-team/"]
            }
        },
        {
            resolve: "gatsby-plugin-google-tagmanager",
            options: {
                id: process.env.GOOGLE_TAGMANAGER_ID
            }
        }
    ]
};
<commit_msg>chore: remove dotenv environment configs<commit_after>require("dotenv").config();

module.exports = {
    siteMetadata: {
        siteUrl: "https://mondora.com",
        title: ":mondora - Building software, creating benefit",
        description:
            "Software and advisory company specialized in custom cloud solutions. Our aim is to create benefit for all stakeholders by designing and building software solutions that maximise positive impact.",
        author: "mondora-team"
    },
    plugins: [
        "gatsby-plugin-transition-link",
        "gatsby-plugin-react-helmet",
        "gatsby-plugin-styled-components",
        "gatsby-transformer-sharp",
        "gatsby-plugin-sharp",
        {
            resolve: "gatsby-source-rss-feed",
            options: {
                url: "https://bcalmbcorp.com/feed/",
                name: "BcalmBcorp"
            }
        },
        {
            resolve: "gatsby-plugin-manifest",
            options: {
                icon: "src/images/logo/square.png",
                name: ":mondora",
                short_name: ":m",
                start_url: "/",
                background_color: "#f2f2f2",
                theme_color: "#ffda01",
                display: "minimal-ui"
            }
        },
        {
            resolve: "gatsby-source-filesystem",
            options: {
                name: "images",
                path: `${__dirname}/src/images`
            }
        },
        {
            resolve: "gatsby-source-instagram",
            options: {
                username: "mondoracom"
            }
        },
        {
            resolve: "gatsby-plugin-sitemap",
            options: {
                exclude: ["/meet-the-team/"]
            }
        },
        {
            resolve: "gatsby-plugin-google-tagmanager",
            options: {
                id: process.env.GOOGLE_TAGMANAGER_ID
            }
        }
    ]
};
<|endoftext|>


<commit_before>function BaseUIWindow(title) {
	var self = Ti.UI.createWindow({
		title:title,
		backgroundColor:'white'
	});

	// create table view data object
	var data = [
		{title:'Tab Groups', hasChild:true, test:'ui/common/baseui/tab_groups'},
		{title:'Window Properties', hasChild:true, test:'ui/common/baseui/window_properties'},
		{title:'Window Layout', hasChild:true, test:'ui/common/baseui/window_layout'},
		{title:'Window (Standalone)', hasChild:true, test:'ui/common/baseui/window_standalone'},
		{title:'Views', hasChild:true, test:'ui/common/baseui/views'},
		{title:'Custom Events', hasChild:true, test:'ui/common/baseui/custom_events'},
		{title:'Window Events', hasChild:true, test:'ui/common/baseui/window_events'},
		{title:'Vertical Layout', hasChild:true, test:'ui/common/baseui/vertical_layout'},
		{title:'Horizontal Layout', hasChild:true, test:'ui/common/baseui/horizontal_layout'}
	];

	if (Ti.Platform.osname == 'mobileweb') {
		data.shift();
	}

	// add iphone specific tests
	if (Titanium.Platform.name == 'iPhone OS')
	{
		data.push({title:'Tabs', hasChild:true, test:'ui/handheld/ios/baseui/tabs'});
		data.push({title:'Window NavBar', hasChild:true, test:'ui/handheld/ios/baseui/window_navbar'});
		data.push({title:'Window Toolbar', hasChild:true, test:'ui/handheld/ios/baseui/window_toolbar'});
		data.push({title:'Window Constructor', hasChild:true, test:'ui/handheld/ios/baseui/window_constructor'});
		data.push({title:'Animation', hasChild:true, test:'ui/handheld/ios/baseui/animation'});
		data.push({title:'Nav Group', hasChild:true, test:'ui/handheld/ios/baseui/navgroup'});

		Ti.include("/etc/version.js");

		if (isIPhone3_2_Plus())
		{
			data.push({title:'Modal Windows', hasChild:true, test:'ui/handheld/ios/baseui/modal_windows'});
			data.push({title:'Custom Fonts', hasChild:true, test:'ui/handheld/ios/baseui/custom_fonts'});
		}
	}

	// add android specific tests
	if (Titanium.Platform.osname == 'android')
	{
		data.push({title:'Preferences', hasChild:true, test:'ui/handheld/android/baseui/preferences'});
	    data.push({title:'Hide Soft Keyboard (Android)', hasChild:true, test:'ui/handheld/android/baseui/android_hide_softkeyboard'});
	    data.push({title: 'Window Soft Input (Android)', hasChild:true, test:'ui/handheld/android/baseui/android_window_soft_input_mode'});
	    data.push({title: 'Menu (Android)', hasChild:true, test:'ui/handheld/android/baseui/android_menus'});
	}

	// create table view
	var tableview = Titanium.UI.createTableView({
		data:data
	});

	// create table view event listener
	tableview.addEventListener('click', function(e)
	{
		if (e.rowData.test)
		{
			var ExampleWindow = require(e.rowData.test);
				win = new ExampleWindow({title:e.rowData.title,containingTab:self.containingTab,tabGroup:self.tabGroup});
			if (Ti.Platform.name == "android") {

			} else {
				win.backgroundColor = "#fff";
				win.barColor = "#111";
			}

			if (e.index == 3)
			{
				if (Ti.Platform.name == "iPhone OS") {
					win.hideTabBar();
				}
			}
			if (Ti.Platform.name==='android' && e.rowData.test.indexOf('window_properties') >= 0) {
				// As explained in apidoc for Window, if opacity is ever to be changed for an Android
				// activity during its lifetime, it needs to use a translucent background.  We trigger
				// using a translucent theme by the presence of the opacity property, so we need to
				// set it here.  Setting it to 1 means it's totally opaque, but gives us the property to
				// make it more transparent later with the "toggle opacity" test.
				win.backgroundColor = "#191919"
				win.opacity = 1;
			}
			self.containingTab.open(win,{animated:true});
		}
	});

	// add table view to the window
	self.add(tableview);

	self.addEventListener('focus', function()
	{
		Ti.API.info('FOCUS RECEIVED IN base_ui');

		if (Ti.Platform.osname !== 'mobileweb') {
			Ti.API.info(Ti.dumpCoverage());
		}
	});
	//
	//  ADD EVENT LISTENERS FOR CUSTOM EVENTS
	//
	var win = Titanium.UI.createWindow({
		height:30,
		width:250,
		bottom:110,
		borderRadius:10
	});

	var view = Titanium.UI.createView({
		backgroundColor:'#000',
		opacity:0.7,
		height:30,
		width:250,
		borderRadius:10
	});

	var label = Titanium.UI.createLabel({
		color:'#fff',
		font:{fontSize:13},
		textAlign:'center',
		width:'auto',
		height:'auto'
	});
	win.add(view);
	win.add(label);

	Titanium.App.addEventListener('event_one', function(e)
	{
		label.text = 'base_ui.js: event one, array length = ' + e.data.length;
		win.open();
		setTimeout(function()
		{
			win.close({opacity:0,duration:500});
		},1000);
	});

	Titanium.App.addEventListener('event_two', function(e)
	{
		label.text = 'base_ui.js: event two, name = ' + e.name;
		win.open();
		setTimeout(function()
		{
			win.close({opacity:0,duration:500});
		},1000);

	});

	return self;
};

module.exports = BaseUIWindow;
<commit_msg>Re-adding TabGroup example for MobileWeb<commit_after>function BaseUIWindow(title) {
	var self = Ti.UI.createWindow({
		title:title,
		backgroundColor:'white'
	});

	// create table view data object
	var data = [
		{title:'Tab Groups', hasChild:true, test:'ui/common/baseui/tab_groups'},
		{title:'Window Properties', hasChild:true, test:'ui/common/baseui/window_properties'},
		{title:'Window Layout', hasChild:true, test:'ui/common/baseui/window_layout'},
		{title:'Window (Standalone)', hasChild:true, test:'ui/common/baseui/window_standalone'},
		{title:'Views', hasChild:true, test:'ui/common/baseui/views'},
		{title:'Custom Events', hasChild:true, test:'ui/common/baseui/custom_events'},
		{title:'Window Events', hasChild:true, test:'ui/common/baseui/window_events'},
		{title:'Vertical Layout', hasChild:true, test:'ui/common/baseui/vertical_layout'},
		{title:'Horizontal Layout', hasChild:true, test:'ui/common/baseui/horizontal_layout'}
	];

	if (Ti.Platform.osname == 'mobileweb') {
		//data.shift();
	}

	// add iphone specific tests
	if (Titanium.Platform.name == 'iPhone OS')
	{
		data.push({title:'Tabs', hasChild:true, test:'ui/handheld/ios/baseui/tabs'});
		data.push({title:'Window NavBar', hasChild:true, test:'ui/handheld/ios/baseui/window_navbar'});
		data.push({title:'Window Toolbar', hasChild:true, test:'ui/handheld/ios/baseui/window_toolbar'});
		data.push({title:'Window Constructor', hasChild:true, test:'ui/handheld/ios/baseui/window_constructor'});
		data.push({title:'Animation', hasChild:true, test:'ui/handheld/ios/baseui/animation'});
		data.push({title:'Nav Group', hasChild:true, test:'ui/handheld/ios/baseui/navgroup'});

		Ti.include("/etc/version.js");

		if (isIPhone3_2_Plus())
		{
			data.push({title:'Modal Windows', hasChild:true, test:'ui/handheld/ios/baseui/modal_windows'});
			data.push({title:'Custom Fonts', hasChild:true, test:'ui/handheld/ios/baseui/custom_fonts'});
		}
	}

	// add android specific tests
	if (Titanium.Platform.osname == 'android')
	{
		data.push({title:'Preferences', hasChild:true, test:'ui/handheld/android/baseui/preferences'});
	    data.push({title:'Hide Soft Keyboard (Android)', hasChild:true, test:'ui/handheld/android/baseui/android_hide_softkeyboard'});
	    data.push({title: 'Window Soft Input (Android)', hasChild:true, test:'ui/handheld/android/baseui/android_window_soft_input_mode'});
	    data.push({title: 'Menu (Android)', hasChild:true, test:'ui/handheld/android/baseui/android_menus'});
	}

	// create table view
	var tableview = Titanium.UI.createTableView({
		data:data
	});

	// create table view event listener
	tableview.addEventListener('click', function(e)
	{
		if (e.rowData.test)
		{
			var ExampleWindow = require(e.rowData.test);
				win = new ExampleWindow({title:e.rowData.title,containingTab:self.containingTab,tabGroup:self.tabGroup});
			if (Ti.Platform.name == "android") {

			} else {
				win.backgroundColor = "#fff";
				win.barColor = "#111";
			}

			if (e.index == 3)
			{
				if (Ti.Platform.name == "iPhone OS") {
					win.hideTabBar();
				}
			}
			if (Ti.Platform.name==='android' && e.rowData.test.indexOf('window_properties') >= 0) {
				// As explained in apidoc for Window, if opacity is ever to be changed for an Android
				// activity during its lifetime, it needs to use a translucent background.  We trigger
				// using a translucent theme by the presence of the opacity property, so we need to
				// set it here.  Setting it to 1 means it's totally opaque, but gives us the property to
				// make it more transparent later with the "toggle opacity" test.
				win.backgroundColor = "#191919"
				win.opacity = 1;
			}
			self.containingTab.open(win,{animated:true});
		}
	});

	// add table view to the window
	self.add(tableview);

	self.addEventListener('focus', function()
	{
		Ti.API.info('FOCUS RECEIVED IN base_ui');

		if (Ti.Platform.osname !== 'mobileweb') {
			Ti.API.info(Ti.dumpCoverage());
		}
	});
	//
	//  ADD EVENT LISTENERS FOR CUSTOM EVENTS
	//
	var win = Titanium.UI.createWindow({
		height:30,
		width:250,
		bottom:110,
		borderRadius:10
	});

	var view = Titanium.UI.createView({
		backgroundColor:'#000',
		opacity:0.7,
		height:30,
		width:250,
		borderRadius:10
	});

	var label = Titanium.UI.createLabel({
		color:'#fff',
		font:{fontSize:13},
		textAlign:'center',
		width:'auto',
		height:'auto'
	});
	win.add(view);
	win.add(label);

	Titanium.App.addEventListener('event_one', function(e)
	{
		label.text = 'base_ui.js: event one, array length = ' + e.data.length;
		win.open();
		setTimeout(function()
		{
			win.close({opacity:0,duration:500});
		},1000);
	});

	Titanium.App.addEventListener('event_two', function(e)
	{
		label.text = 'base_ui.js: event two, name = ' + e.name;
		win.open();
		setTimeout(function()
		{
			win.close({opacity:0,duration:500});
		},1000);

	});

	return self;
};

module.exports = BaseUIWindow;
<|endoftext|>


<commit_before>'use strict';

/**
 * prepublish -- Runs all scripts defined in `.npmscriptrc` `publish.prepublish.scripts` entry, but only if
 * pre-publish mode is true. Node / NPM currently has a bug (https://github.com/npm/npm/issues/10074) that will run the
 * `prepublish` script when modules are installed or `npm pack` is executed. In these cases it is not desirable to run
 * actual pre-publish actions. NPM module `in-publish` provides pre-publish detection.
 *
 * `.npmscriptrc` must be defined in the root path and contain an object hash `publish` with a `prepublish` hash
 * with the following options:
 * ```
 * (Array<string>)   scripts - An array of executable actions / scripts.
 * ```
 */

var inPublish =   require('in-publish').inPublish();

if (inPublish || process.env.NPM_IN_PUBLISH_TEST)
{
   var cp =                require('child_process');
   var fs =                require('fs');
   var stripJsonComments = require('strip-json-comments');

   // Verify that `.npmscriptrc` exists.
   try
   {
      if (!fs.statSync('./.npmscriptrc').isFile())
      {
         throw new Error("'.npmscriptrc' not found in root path.");
      }
   }
   catch(err)
   {
      throw new Error("TyphonJS NPM script (prepublish) error: " + err);
   }

   // Load `.npmscriptrc` and strip comments.
   var configInfo = JSON.parse(stripJsonComments(fs.readFileSync('./.npmscriptrc', 'utf-8')));

   // Verify that publish entry is an object.
   if (typeof configInfo.publish !== 'object')
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish' entry is not an object or is missing in "
       + "'.npmscriptrc'.");
   }

   // Verify that prepublish entry is an object.
   if (typeof configInfo.publish.prepublish !== 'object')
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish.prepublish' entry is not an object or is missing in "
        + "'.npmscriptrc'.");
   }

   var prepublishConfig = configInfo.publish.prepublish;

   // Verify scripts entry exists
   if (typeof prepublishConfig.scripts === 'undefined')
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish.prepublish.scripts' entry is missing in '.npmscriptrc'.");
   }

   if (!Array.isArray(prepublishConfig.scripts))
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish.prepublish.scripts' entry is not an array in "
        + "'.npmscriptrc'.");
   }

   // Execute scripts
   for (var cntr = 0; cntr < prepublishConfig.scripts.length; cntr++)
   {
      // Build base execution command.
      var exec = prepublishConfig.scripts[cntr];

      // Notify what command is being executed then execute it.
      process.stdout.write('Prepublish executing: ' + exec + '\n');
      cp.execSync(exec, { stdio: 'inherit' });
   }
}<commit_msg>0.1.0 release<commit_after>'use strict';

/**
 * prepublish -- Runs all scripts defined in `.npmscriptrc` `publish.prepublish.scripts` entry, but only if
 * pre-publish mode is true. Node / NPM currently has a bug (https://github.com/npm/npm/issues/10074) that will run the
 * `prepublish` script when modules are installed or `npm pack` is executed. In these cases it is not desirable to run
 * actual pre-publish actions. NPM module `in-publish` provides pre-publish detection.
 *
 * `.npmscriptrc` must be defined in the root path and contain an object hash `publish` with a `prepublish` hash
 * with the following options:
 * ```
 * (Array<string>)   scripts - An array of executable actions / scripts.
 * ```
 */

var inPublish =   require('in-publish').inPublish();

if (inPublish || process.env.NPM_IN_PUBLISH_TEST)
{
   var cp =                require('child_process');
   var fs =                require('fs');
   var stripJsonComments = require('strip-json-comments');

   // Verify that `.npmscriptrc` exists.
   /* istanbul ignore next */
   try
   {
      if (!fs.statSync('./.npmscriptrc').isFile())
      {
         throw new Error("'.npmscriptrc' not found in root path.");
      }
   }
   catch(err)
   {
      throw new Error("TyphonJS NPM script (prepublish) error: " + err);
   }

   // Load `.npmscriptrc` and strip comments.
   var configInfo = JSON.parse(stripJsonComments(fs.readFileSync('./.npmscriptrc', 'utf-8')));

   // Verify that publish entry is an object.
   /* istanbul ignore if */
   if (typeof configInfo.publish !== 'object')
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish' entry is not an object or is missing in "
       + "'.npmscriptrc'.");
   }

   // Verify that prepublish entry is an object.
   /* istanbul ignore if */
   if (typeof configInfo.publish.prepublish !== 'object')
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish.prepublish' entry is not an object or is missing in "
        + "'.npmscriptrc'.");
   }

   var prepublishConfig = configInfo.publish.prepublish;

   // Verify scripts entry exists
   /* istanbul ignore if */
   if (typeof prepublishConfig.scripts === 'undefined')
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish.prepublish.scripts' entry is missing in '.npmscriptrc'.");
   }

   /* istanbul ignore if */
   if (!Array.isArray(prepublishConfig.scripts))
   {
      throw new Error(
       "TyphonJS NPM script (prepublish) error: 'publish.prepublish.scripts' entry is not an array in "
        + "'.npmscriptrc'.");
   }

   // Execute scripts
   for (var cntr = 0; cntr < prepublishConfig.scripts.length; cntr++)
   {
      // Build base execution command.
      var exec = prepublishConfig.scripts[cntr];

      // Notify what command is being executed then execute it.
      process.stdout.write('Prepublish executing: ' + exec + '\n');
      cp.execSync(exec, { stdio: 'inherit' });
   }
}<|endoftext|>


<commit_before>import {getBaseURL} from '../utils'

class API {
    constructor() {
        this.baseURL = getBaseURL()
    }

    fetch(url, object, callback) {
        fetch(this.baseURL + url, object)
            .then(response => {
                if (response.ok) return response.json()
                return response.json()
            })
            .then(json => {
                return callback(json)
            })
            .catch(error => {
                console.log("API error", error)
                return callback(null)
            })
    }

    getRoutes(callback) {
        this.fetch('/api/routes', {method: 'get'}, callback)
    }

    postRoute(route, callback) {
        this.fetch('/api/routes', {
            method: 'post',
            body: JSON.stringify(route),
            headers: new Headers({'Content-Type': 'application/json'})
        }, callback)
    }

    putRoute(route, callback) {
        this.fetch('/api/routes/' + route.name, {
            method: 'put',
            body: JSON.stringify(route),
            headers: new Headers({'Content-Type': 'application/json'})
        }, callback)
    }

    reorderRoutes(rangeStart, rangeLength, insertBefore, callback) {
        this.fetch('/api/routes', {
            method: 'put',
            body: JSON.stringify({
                'range_start': rangeStart,
                'range_length': rangeLength,
                'insert_before': insertBefore
            }),
            headers: new Headers({'Content-Type': 'application/json'})
        }, callback)
    }

    deleteRoute(name, callback) {
        this.fetch('/api/routes/' + name, {method: 'delete'}, callback)
    }

    postMessage(to, from, body, callback) {
        this.fetch('/api/messages', {
                method: 'post',
                body: JSON.stringify({
                    'to': [to],
                    'from': from,
                    'body': body
                }),
                headers: new Headers({'Content-Type': 'application/json'})
            }, callback)
    }

    getMessages(query, callback) {
        this.fetch('/api/messages' + query, {method: 'get'}, callback)
    }

    getMessagesByIds(messageIds, callback) {
        this.fetch('/api/messages/byIds?ids=' + messageIds, {method: 'get'}, callback)
    }
}

const api = new API()

export default api
export {API}<commit_msg>Fix API.js<commit_after>import {getBaseURL} from '../utils'

class API {
    constructor() {
        this.baseURL = getBaseURL()
    }

    fetch(url, object, callback) {
        fetch(this.baseURL + url, object)
            .then(response => {
                if (response.ok) return response.json()
                return response.json()
            })
            .then(json => {
                return callback(json)
            })
            .catch(error => {
                return callback(null)
            })
    }

    getRoutes(callback) {
        this.fetch('/api/routes', {method: 'get'}, callback)
    }

    postRoute(route, callback) {
        this.fetch('/api/routes', {
            method: 'post',
            body: JSON.stringify(route),
            headers: new Headers({'Content-Type': 'application/json'})
        }, callback)
    }

    putRoute(route, callback) {
        this.fetch('/api/routes/' + route.name, {
            method: 'put',
            body: JSON.stringify(route),
            headers: new Headers({'Content-Type': 'application/json'})
        }, callback)
    }

    reorderRoutes(rangeStart, rangeLength, insertBefore, callback) {
        this.fetch('/api/routes', {
            method: 'put',
            body: JSON.stringify({
                'range_start': rangeStart,
                'range_length': rangeLength,
                'insert_before': insertBefore
            }),
            headers: new Headers({'Content-Type': 'application/json'})
        }, callback)
    }

    deleteRoute(name, callback) {
        this.fetch('/api/routes/' + name, {method: 'delete'}, callback)
    }

    postMessage(to, from, body, callback) {
        this.fetch('/api/messages', {
                method: 'post',
                body: JSON.stringify({
                    'to': [to],
                    'from': from,
                    'body': body
                }),
                headers: new Headers({'Content-Type': 'application/json'})
            }, callback)
    }

    getMessages(query, callback) {
        this.fetch('/api/messages' + query, {method: 'get'}, callback)
    }

    getMessagesByIds(messageIds, callback) {
        this.fetch('/api/messages/byIds?ids=' + messageIds, {method: 'get'}, callback)
    }
}

const api = new API()

export default api
export {API}<|endoftext|>


<commit_before>angular.module('angular-meteor.reactive', [
  'angular-meteor.utils',
  'angular-meteor.mixer',
  'angular-meteor.core',
  'angular-meteor.view-model'
])

/*
  A mixin which enhance our reactive abilities by providing methods that are capable of updating
  our scope reactively.
 */
.factory('$$Reactive', [
  '$parse',
  '$$utils',
  '$angularMeteorSettings',

function($parse, $$utils, $angularMeteorSettings) {
  function $$Reactive(vm = this) {
    // Helps us track changes made in the view model
    vm.$$dependencies = {};
  }

  // Gets an object containing functions and define their results as reactive properties.
  // Once a return value has been changed the property will be reset.
  $$Reactive.helpers = function(props = {}) {
    if (!_.isObject(props))
      throw Error('argument 1 must be an object');

    _.each(props, (v, k, i) => {
      if (!_.isFunction(v))
        throw Error(`helper ${i + 1} must be a function`);

      if (!this.$$vm.$$dependencies[k])
        // Registers a new dependency to the specified helper
        this.$$vm.$$dependencies[k] = new Tracker.Dependency();

      this.$$setFnHelper(k, v);
    });
  };

  // Gets a model reactively
  $$Reactive.getReactively = function(k, isDeep = false) {
    if (!_.isBoolean(isDeep))
      throw Error('argument 2 must be a boolean');

    return this.$$reactivateEntity(k, this.$watch, isDeep);
  };

  // Gets a collection reactively
  $$Reactive.getCollectionReactively = function(k) {
    return this.$$reactivateEntity(k, this.$watchCollection);
  };

  // Gets an entity reactively, and once it has been changed the computation will be recomputed
  $$Reactive.$$reactivateEntity = function(k, watcher, ...watcherArgs) {
    if (!_.isString(k))
      throw Error('argument 1 must be a string');

    if (!this.$$vm.$$dependencies[k]) {
      this.$$vm.$$dependencies[k] = new Tracker.Dependency();
      this.$$watchEntity(k, watcher, ...watcherArgs);
    }

    this.$$vm.$$dependencies[k].depend();
    return $parse(k)(this.$$vm);
  };

  // Watches for changes in the view model, and if so will notify a change
  $$Reactive.$$watchEntity = function(k, watcher, ...watcherArgs) {
    // Gets a deep property from the view model
    let getVal = _.partial($parse(k), this.$$vm);
    let initialVal = getVal();

    // Watches for changes in the view model
    watcher.call(this, getVal, (val, oldVal) => {
      let hasChanged =
        val !== initialVal ||
        val !== oldVal;

      // Notify if a change has been detected
      if (hasChanged) this.$$changed(k);
    }, ...watcherArgs);
  };

  // Invokes a function and sets the return value as a property
  $$Reactive.$$setFnHelper = function(k, fn) {
    this.autorun((computation) => {
      // Invokes the reactive functon
      let model = fn.apply(this.$$vm);

      // Ignore notifications made by the following handler
      Tracker.nonreactive(() => {
        // If a cursor, observe its changes and update acoordingly
        if ($$utils.isCursor(model)) {
          let observation = this.$$handleCursor(k, model);

          computation.onInvalidate(() => {
            observation.stop();
            this.$$vm[k].splice(0);
          });
        }
        else {
          this.$$handleNonCursor(k, model);
        }

        // Notify change and update the view model
        this.$$changed(k);
      });
    });
  };

  // Sets a value helper as a setter and a getter which will notify computations once used
  $$Reactive.$$setValHelper = function(k, v, watch = true) {
    // If set, reactives property
    if (watch) {
      let isDeep = _.isObject(v);
      this.getReactively(k, isDeep);
    }

    Object.defineProperty(this.$$vm, k, {
      configurable: true,
      enumerable: true,

      get: () => {
        this.$$depend(k);
        return v;
      },
      set: (newVal) => {
        v = newVal;
        this.$$changed(k);
      }
    });
  };

  // Fetching a cursor and updates properties once the result set has been changed
  $$Reactive.$$handleCursor = function(k, cursor) {
    // If not defined set it
    if (angular.isUndefined(this.$$vm[k])) {
      this.$$setValHelper(k, cursor.fetch(), false);
    }
    // If defined update it
    else {
      let diff = jsondiffpatch.diff(this.$$vm[k], cursor.fetch());
      jsondiffpatch.patch(this.$$vm[k], diff);
    }

    // Observe changes made in the result set
    let observation = cursor.observe({
      addedAt: (doc, atIndex) => {
        if (!observation) return;
        this.$$vm[k].splice(atIndex, 0, doc);
        this.$$changed(k);
      },
      changedAt: (doc, oldDoc, atIndex) => {
        let diff = jsondiffpatch.diff(this.$$vm[k][atIndex], doc);
        jsondiffpatch.patch(this.$$vm[k][atIndex], diff);
        this.$$changed(k);
      },
      movedTo: (doc, fromIndex, toIndex) => {
        this.$$vm[k].splice(fromIndex, 1);
        this.$$vm[k].splice(toIndex, 0, doc);
        this.$$changed(k);
      },
      removedAt: (oldDoc, atIndex) => {
        this.$$vm[k].splice(atIndex, 1);
        this.$$changed(k);
      }
    });

    return observation;
  };

  $$Reactive.$$handleNonCursor = function(k, data) {
    let v = this.$$vm[k];

    if (angular.isDefined(v)) {
      if (!$angularMeteorSettings.suppressWarnings)
        console.warn(`overriding '${k}' helper. You can disable this warning by following this guide http://www.angular-meteor.com/api/1.3.6/settings`);
      delete this.$$vm[k];
      v = null;
    }

    if (angular.isUndefined(v)) {
      this.$$setValHelper(k, data);
    }
    // Update property if the new value is from the same type
    else if ($$utils.areSiblings(v, data)) {
      let diff = jsondiffpatch.diff(v, data);
      jsondiffpatch.patch(v, diff);
      this.$$changed(k);
    }
    else {
      this.$$vm[k] = data;
    }
  };

  // Notifies dependency in view model
  $$Reactive.$$depend = function(k) {
    this.$$vm.$$dependencies[k].depend();
  };

  // Notifies change in view model
  $$Reactive.$$changed = function(k) {
    this.$$throttledDigest();
    this.$$vm.$$dependencies[k].changed();
  };

  return $$Reactive;
}]);
<commit_msg>fix($reactive): don't depend outside of computation<commit_after>angular.module('angular-meteor.reactive', [
  'angular-meteor.utils',
  'angular-meteor.mixer',
  'angular-meteor.core',
  'angular-meteor.view-model'
])

/*
  A mixin which enhance our reactive abilities by providing methods that are capable of updating
  our scope reactively.
 */
.factory('$$Reactive', [
  '$parse',
  '$$utils',
  '$angularMeteorSettings',

function($parse, $$utils, $angularMeteorSettings) {
  function $$Reactive(vm = this) {
    // Helps us track changes made in the view model
    vm.$$dependencies = {};
  }

  // Gets an object containing functions and define their results as reactive properties.
  // Once a return value has been changed the property will be reset.
  $$Reactive.helpers = function(props = {}) {
    if (!_.isObject(props))
      throw Error('argument 1 must be an object');

    _.each(props, (v, k, i) => {
      if (!_.isFunction(v))
        throw Error(`helper ${i + 1} must be a function`);

      if (!this.$$vm.$$dependencies[k])
        // Registers a new dependency to the specified helper
        this.$$vm.$$dependencies[k] = new Tracker.Dependency();

      this.$$setFnHelper(k, v);
    });
  };

  // Gets a model reactively
  $$Reactive.getReactively = function(k, isDeep = false) {
    if (!_.isBoolean(isDeep))
      throw Error('argument 2 must be a boolean');

    return this.$$reactivateEntity(k, this.$watch, isDeep);
  };

  // Gets a collection reactively
  $$Reactive.getCollectionReactively = function(k) {
    return this.$$reactivateEntity(k, this.$watchCollection);
  };

  // Gets an entity reactively, and once it has been changed the computation will be recomputed
  $$Reactive.$$reactivateEntity = function(k, watcher, ...watcherArgs) {
    if (!_.isString(k))
      throw Error('argument 1 must be a string');

    if (!this.$$vm.$$dependencies[k]) {
      this.$$vm.$$dependencies[k] = new Tracker.Dependency();
      this.$$watchEntity(k, watcher, ...watcherArgs);
    }

    this.$$vm.$$dependencies[k].depend();
    return $parse(k)(this.$$vm);
  };

  // Watches for changes in the view model, and if so will notify a change
  $$Reactive.$$watchEntity = function(k, watcher, ...watcherArgs) {
    // Gets a deep property from the view model
    let getVal = _.partial($parse(k), this.$$vm);
    let initialVal = getVal();

    // Watches for changes in the view model
    watcher.call(this, getVal, (val, oldVal) => {
      let hasChanged =
        val !== initialVal ||
        val !== oldVal;

      // Notify if a change has been detected
      if (hasChanged) this.$$changed(k);
    }, ...watcherArgs);
  };

  // Invokes a function and sets the return value as a property
  $$Reactive.$$setFnHelper = function(k, fn) {
    this.autorun((computation) => {
      // Invokes the reactive functon
      let model = fn.apply(this.$$vm);

      // Ignore notifications made by the following handler
      Tracker.nonreactive(() => {
        // If a cursor, observe its changes and update acoordingly
        if ($$utils.isCursor(model)) {
          let observation = this.$$handleCursor(k, model);

          computation.onInvalidate(() => {
            observation.stop();
            this.$$vm[k].splice(0);
          });
        }
        else {
          this.$$handleNonCursor(k, model);
        }

        // Notify change and update the view model
        this.$$changed(k);
      });
    });
  };

  // Sets a value helper as a setter and a getter which will notify computations once used
  $$Reactive.$$setValHelper = function(k, v, watch = true) {
    // If set, reactives property
    if (watch) {
      let isDeep = _.isObject(v);
      this.getReactively(k, isDeep);
    }

    Object.defineProperty(this.$$vm, k, {
      configurable: true,
      enumerable: true,

      get: () => {
        return v;
      },
      set: (newVal) => {
        v = newVal;
        this.$$changed(k);
      }
    });
  };

  // Fetching a cursor and updates properties once the result set has been changed
  $$Reactive.$$handleCursor = function(k, cursor) {
    // If not defined set it
    if (angular.isUndefined(this.$$vm[k])) {
      this.$$setValHelper(k, cursor.fetch(), false);
    }
    // If defined update it
    else {
      let diff = jsondiffpatch.diff(this.$$vm[k], cursor.fetch());
      jsondiffpatch.patch(this.$$vm[k], diff);
    }

    // Observe changes made in the result set
    let observation = cursor.observe({
      addedAt: (doc, atIndex) => {
        if (!observation) return;
        this.$$vm[k].splice(atIndex, 0, doc);
        this.$$changed(k);
      },
      changedAt: (doc, oldDoc, atIndex) => {
        let diff = jsondiffpatch.diff(this.$$vm[k][atIndex], doc);
        jsondiffpatch.patch(this.$$vm[k][atIndex], diff);
        this.$$changed(k);
      },
      movedTo: (doc, fromIndex, toIndex) => {
        this.$$vm[k].splice(fromIndex, 1);
        this.$$vm[k].splice(toIndex, 0, doc);
        this.$$changed(k);
      },
      removedAt: (oldDoc, atIndex) => {
        this.$$vm[k].splice(atIndex, 1);
        this.$$changed(k);
      }
    });

    return observation;
  };

  $$Reactive.$$handleNonCursor = function(k, data) {
    let v = this.$$vm[k];

    if (angular.isDefined(v)) {
      if (!$angularMeteorSettings.suppressWarnings)
        console.warn(`overriding '${k}' helper. You can disable this warning by following this guide http://www.angular-meteor.com/api/1.3.6/settings`);
      delete this.$$vm[k];
      v = null;
    }

    if (angular.isUndefined(v)) {
      this.$$setValHelper(k, data);
    }
    // Update property if the new value is from the same type
    else if ($$utils.areSiblings(v, data)) {
      let diff = jsondiffpatch.diff(v, data);
      jsondiffpatch.patch(v, diff);
      this.$$changed(k);
    }
    else {
      this.$$vm[k] = data;
    }
  };

  // Notifies dependency in view model
  $$Reactive.$$depend = function(k) {
    this.$$vm.$$dependencies[k].depend();
  };

  // Notifies change in view model
  $$Reactive.$$changed = function(k) {
    this.$$throttledDigest();
    this.$$vm.$$dependencies[k].changed();
  };

  return $$Reactive;
}]);
<|endoftext|>