<commit_before>var findRoot = require('find-root')
  , path = require('path')
  , resolve = require('resolve')
  , get = require('lodash.get')
  , find = require('array-find')

var resolveAlias = require('./resolve-alias')

/**
 * Find the full path to 'source', given 'file' as a full reference path.
 *
 * resolveImport('./foo', '/Users/ben/bar.js') => '/Users/ben/foo.js'
 * @param  {string} source - the module to resolve; i.e './some-module'
 * @param  {string} file - the importing file's full path; i.e. '/usr/local/bin/file.js'
 * TODO: take options as a third param, with webpack config file name
 * @return {string?} the resolved path to source, undefined if not resolved, or null
 *                   if resolved to a non-FS resource (i.e. script tag at page load)
 */
exports.resolveImport = function resolveImport(source, file, settings) {

  // strip loaders
  var finalBang = source.lastIndexOf('!')
  if (finalBang >= 0) {
    source = source.slice(finalBang + 1)
  }

  if (resolve.isCore(source)) return null

  var webpackConfig
  try {
    var packageDir = findRoot(file)
    if (!packageDir) throw new Error('package not found above ' + file)

    webpackConfig = require(path.join(packageDir, get(settings, 'config', 'webpack.config.js')))
  } catch (err) {
    webpackConfig = {}
  }

  // externals
  if (findExternal(source, webpackConfig.externals)) return null

  // replace alias if needed
  source = resolveAlias(source, get(webpackConfig, ['resolve', 'alias'], {}))

  var paths = []

  // root as first alternate path
  var rootPath = get(webpackConfig, ['resolve', 'root'])
  if (rootPath) paths.push(rootPath)

  // otherwise, resolve "normally"
  return resolve.sync(source, {
    basedir: path.dirname(file),

    // defined via http://webpack.github.io/docs/configuration.html#resolve-extensions
    extensions: get(webpackConfig, ['resolve', 'extensions'])
      || ['', '.webpack.js', '.web.js', '.js'],

    // http://webpack.github.io/docs/configuration.html#resolve-modulesdirectories
    moduleDirectory: get(webpackConfig, ['resolve', 'modulesDirectories'])
      || ['web_modules', 'node_modules'],

    paths: paths,
    packageFilter: packageFilter.bind(null, webpackConfig),
  })
}

function findExternal(source, externals) {
  if (!externals) return false

  // string match
  if (typeof externals === 'string') return (source === externals)

  // array: recurse
  if (externals instanceof Array) {
    return externals.some(function (e) { return findExternal(source, e) })
  }

  if (externals instanceof RegExp) {
    return externals.test(source)
  }

  if (typeof externals === 'function') {
    throw new Error('unable to handle function externals')
  }

  // else, vanilla object
  return Object.keys(externals).some(function (e) { return source === e })
}

/**
 * webpack defaults: http://webpack.github.io/docs/configuration.html#resolve-packagemains
 * @type {Array}
 */
var defaultMains = [
  'webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main',
]

function packageFilter(config, pkg) {
  var altMain

  // check for rollup-style first
  if (pkg['jsnext:main']) {
    pkg['main'] = pkg['jsnext:main']
  } else {
    // check for configured/default alternative main fields
    altMain = find(
      get(config, ['resolve', 'packageMains']) || defaultMains,
      function (m) { return get(pkg, m) })

    if (altMain) {
      pkg['main'] = pkg[altMain]
    }
  }

  return pkg
}
<commit_msg>typeof 'string' check for packageMains, otherwise 'browserify' default key fails hard.<commit_after>var findRoot = require('find-root')
  , path = require('path')
  , resolve = require('resolve')
  , get = require('lodash.get')
  , find = require('array-find')

var resolveAlias = require('./resolve-alias')

/**
 * Find the full path to 'source', given 'file' as a full reference path.
 *
 * resolveImport('./foo', '/Users/ben/bar.js') => '/Users/ben/foo.js'
 * @param  {string} source - the module to resolve; i.e './some-module'
 * @param  {string} file - the importing file's full path; i.e. '/usr/local/bin/file.js'
 * TODO: take options as a third param, with webpack config file name
 * @return {string?} the resolved path to source, undefined if not resolved, or null
 *                   if resolved to a non-FS resource (i.e. script tag at page load)
 */
exports.resolveImport = function resolveImport(source, file, settings) {

  // strip loaders
  var finalBang = source.lastIndexOf('!')
  if (finalBang >= 0) {
    source = source.slice(finalBang + 1)
  }

  if (resolve.isCore(source)) return null

  var webpackConfig
  try {
    var packageDir = findRoot(file)
    if (!packageDir) throw new Error('package not found above ' + file)

    webpackConfig = require(path.join(packageDir, get(settings, 'config', 'webpack.config.js')))
  } catch (err) {
    webpackConfig = {}
  }

  // externals
  if (findExternal(source, webpackConfig.externals)) return null

  // replace alias if needed
  source = resolveAlias(source, get(webpackConfig, ['resolve', 'alias'], {}))

  var paths = []

  // root as first alternate path
  var rootPath = get(webpackConfig, ['resolve', 'root'])
  if (rootPath) paths.push(rootPath)

  // otherwise, resolve "normally"
  return resolve.sync(source, {
    basedir: path.dirname(file),

    // defined via http://webpack.github.io/docs/configuration.html#resolve-extensions
    extensions: get(webpackConfig, ['resolve', 'extensions'])
      || ['', '.webpack.js', '.web.js', '.js'],

    // http://webpack.github.io/docs/configuration.html#resolve-modulesdirectories
    moduleDirectory: get(webpackConfig, ['resolve', 'modulesDirectories'])
      || ['web_modules', 'node_modules'],

    paths: paths,
    packageFilter: packageFilter.bind(null, webpackConfig),
  })
}

function findExternal(source, externals) {
  if (!externals) return false

  // string match
  if (typeof externals === 'string') return (source === externals)

  // array: recurse
  if (externals instanceof Array) {
    return externals.some(function (e) { return findExternal(source, e) })
  }

  if (externals instanceof RegExp) {
    return externals.test(source)
  }

  if (typeof externals === 'function') {
    throw new Error('unable to handle function externals')
  }

  // else, vanilla object
  return Object.keys(externals).some(function (e) { return source === e })
}

/**
 * webpack defaults: http://webpack.github.io/docs/configuration.html#resolve-packagemains
 * @type {Array}
 */
var defaultMains = [
  'webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main',
]

function packageFilter(config, pkg) {
  var altMain

  // check for rollup-style first
  if (pkg['jsnext:main']) {
    pkg['main'] = pkg['jsnext:main']
  } else {
    // check for configured/default alternative main fields
    altMain = find(
      get(config, ['resolve', 'packageMains']) || defaultMains,
      function (m) { return typeof get(pkg, m) === 'string' })

    if (altMain) {
      pkg['main'] = pkg[altMain]
    }
  }

  return pkg
}
<|endoftext|>


<commit_before>var Post = require('../models/post');

/**
 * Expose `Main` Controller.
 */
module.exports = new Main;

/**
 * Main Controller.
 * @constructor
 */
function Main() {}

/**
 * Path to main index (home) page.
 * @private {string}
 * @const
 */
Main.INDEX_VIEW_ = 'main/index';

/**
 * Path to site about view.
 * @private {string}
 * @const
 */
Main.ABOUT_VIEW_ = 'main/about';

/**
 * Path to site contact view.
 * @const
 * @private {string}
 */
Main.CONTACT_VIEW_ = 'main/contact';

/**
 * Path to site login view.
 * @const
 * @private {string}
 */
Main.LOGIN_VIEW_ = 'main/login';

/**
 * Renders main/home index page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.index = function(req, res) {
  var post = new Post(req.app);
  post.find(function(err, results) {
    if (err || !results) {
      req.flash('error', 'There was an error getting the posts: ' + err);
      res.redirect(req.app.get('STATIC_ROUTES').SITE_HOME);
    } else {
      res.render(Main.INDEX_VIEW_, {
        title: 'Home',
        results: results
      });
    }
  });
};

/**
 * Renders main/home about page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.about = function(req, res) {
  res.render(Main.ABOUT_VIEW_, {title: 'About'});
};

/**
 * Renders main/home contact page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.contact = function(req, res) {
  res.render(Main.CONTACT_VIEW_, {title: 'Contact'});
};

/**
 * Renders main/home login page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.login = function(req, res) {
  res.render(Main.LOGIN_VIEW_, {title: 'Login'});
};
<commit_msg>Fixed typo.<commit_after>var Post = require('../models/post');

/**
 * Expose `Main` Controller.
 */
module.exports = new Main;

/**
 * Main Controller.
 * @constructor
 */
function Main() {}

/**
 * Path to main index (home) page.
 * @const
 * @private {string}
 */
Main.INDEX_VIEW_ = 'main/index';

/**
 * Path to site about view.
 * @const
 * @private {string}s
 */
Main.ABOUT_VIEW_ = 'main/about';

/**
 * Path to site contact view.
 * @const
 * @private {string}
 */
Main.CONTACT_VIEW_ = 'main/contact';

/**
 * Path to site login view.
 * @const
 * @private {string}
 */
Main.LOGIN_VIEW_ = 'main/login';

/**
 * Renders main/home index page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.index = function(req, res) {
  var post = new Post(req.app);
  post.find(function(err, results) {
    if (err || !results) {
      req.flash('error', 'There was an error getting the posts: ' + err);
      res.redirect(req.app.get('STATIC_ROUTES').SITE_HOME);
    } else {
      res.render(Main.INDEX_VIEW_, {
        title: 'Home',
        results: results
      });
    }
  });
};

/**
 * Renders main/home about page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.about = function(req, res) {
  res.render(Main.ABOUT_VIEW_, {title: 'About'});
};

/**
 * Renders main/home contact page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.contact = function(req, res) {
  res.render(Main.CONTACT_VIEW_, {title: 'Contact'});
};

/**
 * Renders main/home login page.
 * @param {http.IncomingMessage} req Node/Express request object.
 * @param {http.ServerResponse} res Node/Express response object.
 */
Main.prototype.login = function(req, res) {
  res.render(Main.LOGIN_VIEW_, {title: 'Login'});
};
<|endoftext|>


<commit_before>define(["FileUtils", "EvalUtils", "MemoryArchive", "LocalStorageArchive", "ace/ace", "exampleLog"], function(
    FileUtils,
    EvalUtils,
    MemoryArchive,
    LocalStorageArchive,
    ace,
    exampleLog
) {
    "use strict"

    let Archive = LocalStorageArchive

    // Used for resizing the editor's height
    let dragOriginY

    // Convenience function which examples could use to put up closeable views
    window.show = function(viewFunction, extraStyling) {
        if (!extraStyling) { extraStyling = "" }
        let div = document.createElement("div")

        const ClosableComponent = {
            view() {
                return m("div.ba.ma3.pa3.bg-light-purple" + extraStyling,
                m("button.fr", {onclick: function () { document.body.removeChild(div) } }, "X"),
                viewFunction()
                )
            }
        }

        document.body.appendChild(div)
        m.mount(div, ClosableComponent)
    }

    const WorkspaceView = {
        editor: null,
        lastLoadedContents: "",
        currentItemIndex: null,
        archiveChoice: "local storage",
        savedItemIndexes: {
            "local storage": null,
            "memory": null,
        },
        aceEditorHeight: 20,
        toastMessages: [],

        oninit() {
            if (Archive.itemCount() === 0) {
                window.show(function () {
                    return m("div", `To get started with some example snippets,
                        click "Show example log", then "Load log", then "Next", and then "Do it".
                        Use "Previous" and "Next" to scroll through more example snippets.`)
                })
            }
        },

        changeArchive() {
            const oldChoice = WorkspaceView.archiveChoice
            const newChoice = (WorkspaceView.archiveChoice === "memory" ? "local storage": "memory")

            WorkspaceView.savedItemIndexes[oldChoice] = WorkspaceView.currentItemIndex
            WorkspaceView.archiveChoice = newChoice
            Archive = (newChoice === "memory" ? MemoryArchive : LocalStorageArchive)
            WorkspaceView.currentItemIndex = WorkspaceView.savedItemIndexes[newChoice]
            if (WorkspaceView.currentItemIndex === null) {
                WorkspaceView.setEditorContents("")
            } else {
                WorkspaceView.setEditorContents(Archive.getItem(WorkspaceView.currentItemIndex))
            }
        },

        setEditorContents(newContents, isNotSaved) {
            WorkspaceView.editor.setValue(newContents)
            if (!isNotSaved) {
                WorkspaceView.lastLoadedContents = newContents
            } else {
                WorkspaceView.currentItemIndex = null
            }
            WorkspaceView.editor.selection.clearSelection()
            WorkspaceView.editor.selection.moveCursorFileStart()
        },

        getEditorContents() {
            return WorkspaceView.editor.getValue()
        },

        getSelectedEditorText() {
            let selectedText = WorkspaceView.editor.session.getTextRange(WorkspaceView.editor.getSelectionRange())
            let isNoSelection = false
            if (!selectedText) {
                // assume user wants all text if nothing is selected
                selectedText = WorkspaceView.editor.getValue()
                isNoSelection = true
            }
            return {
                text: selectedText,
                isNoSelection,
            }
        },

        save() {
            const newContents = WorkspaceView.getEditorContents()
            Archive.addItem(newContents)
            WorkspaceView.lastLoadedContents = newContents
            WorkspaceView.currentItemIndex = Archive.itemCount() - 1
        },

        confirmClear(promptText) {
            if (!WorkspaceView.getEditorContents()) return true
            if (WorkspaceView.getEditorContents() === WorkspaceView.lastLoadedContents) return true
            if (!promptText) promptText = "You have unsaved editor changes. Proceed?"
            return confirm(promptText)
        },

        clear() {
            if (!WorkspaceView.confirmClear()) return
            WorkspaceView.setEditorContents("")
            WorkspaceView.currentItemIndex = null
        },

        doIt() {
            const selection = WorkspaceView.getSelectedEditorText()
            try {
                EvalUtils.eval(selection.text)
            } catch (error) {
                WorkspaceView.toast("Eval error:\n" + error)
            }
        },

        printIt() {
            const selection = WorkspaceView.getSelectedEditorText()
            const evalResult = "" + EvalUtils.evalOrError(selection.text)
            if (selection.isNoSelection) { WorkspaceView.editor.selection.moveCursorFileEnd() }
            const selectedRange = WorkspaceView.editor.selection.getRange()
            const start = selectedRange.end
            const end = WorkspaceView.editor.session.insert(start, evalResult)
            WorkspaceView.editor.selection.setRange({start, end})
            WorkspaceView.editor.focus()
        },

        inspectIt() {
            const selection = WorkspaceView.getSelectedEditorText()
            const evalResult = EvalUtils.evalOrError(selection.text)
            console.dir(evalResult)
        },

        importText() {
            if (!WorkspaceView.confirmClear()) return
            FileUtils.loadFromFile((fileName, fileContents) => {
                if (fileContents) {
                    const newContent = fileName + "\n---------------------------------------\n" + fileContents
                    WorkspaceView.setEditorContents(newContent)
                    m.redraw()
                }
            })
        },

        exportText() {
            const fileContents = WorkspaceView.getEditorContents()
            const provisionalFileName = fileContents.split("\n")[0]
            FileUtils.saveToFile(provisionalFileName, fileContents)
        },

        skip(offset) {
            if (!Archive.itemCount()) {
                WorkspaceView.toast("No log items to display. Try saving one first -- or show the example log in the editor and then load it.")
                return
            }
            if (Archive.itemCount() === 1) {
                WorkspaceView.toast("Only one log item to display. Try saving another one first.")
                return
            }
            if (!WorkspaceView.confirmClear()) return
            if (WorkspaceView.currentItemIndex === null) {
                WorkspaceView.currentItemIndex = (offset >= 0 ?  0 : Archive.itemCount() - 1)
            } else {
                WorkspaceView.currentItemIndex = (Archive.itemCount() + WorkspaceView.currentItemIndex + offset) % Archive.itemCount()
            }
            WorkspaceView.setEditorContents(Archive.getItem(WorkspaceView.currentItemIndex))
        },

        previous() { WorkspaceView.skip(-1) },

        next() { WorkspaceView.skip(1) },

        showLog() {
            if (!WorkspaceView.confirmClear()) return
            WorkspaceView.setEditorContents(Archive.textForLog())
            WorkspaceView.currentItemIndex = null
        },

        loadLog() {
            if (Archive.itemCount() && !confirm("Replace all items with entered text for a log?")) return
            try {
                Archive.loadFromLogText(WorkspaceView.getEditorContents())
            } catch (error) {
                WorkspaceView.toast("Problem loading log from editor:\n" + error)
                return
            }
            WorkspaceView.currentItemIndex = null
            // Update lastLoadedContents in case pasted in contents to avoid warning later since data was processed as intended
            WorkspaceView.lastLoadedContents = WorkspaceView.getEditorContents()
            WorkspaceView.toast("Loaded log from editor")
        },

        showExampleLog() {
            if (!WorkspaceView.confirmClear()) return
            WorkspaceView.setEditorContents(JSON.stringify(exampleLog, null, 4))
        },

        toast(message, delay) {
            function removeToastAfterDelay() {
                setTimeout(function() {
                    WorkspaceView.toastMessages.shift()
                    if ( WorkspaceView.toastMessages.length ) { removeToastAfterDelay() }
                    m.redraw()
                }, WorkspaceView.toastMessages[0].delay)
            }
            if (delay === undefined) { delay = 3000 }
            WorkspaceView.toastMessages.push({message, delay})
            if ( WorkspaceView.toastMessages.length === 1) { removeToastAfterDelay() }
        },

        view() {
            return m("main.ma2", [
                m("div#toastDiv.fixed.top-2.left-2.w-40.pa2.fieldset.bg-gold.pl3.pr3.tc.o-90.z-max",
                    { hidden: WorkspaceView.toastMessages.length === 0 },
                    WorkspaceView.toastMessages.length ? WorkspaceView.toastMessages[0].message : ""
                ),
                m("button", {onclick: WorkspaceView.changeArchive}, "Archive: " + WorkspaceView.archiveChoice),
                m("h4.bw24.b--solid.b--blue.pa1",
                    "JavaScript snippet ",
                    (WorkspaceView.currentItemIndex === null ? "???" : WorkspaceView.currentItemIndex + 1),
                    " of ",
                    Archive.itemCount()
                ),
                m("input#fileInput", { "type" : "file" , "hidden" : true } ),
                m("div.w-90#editor", {
                    style: {
                        height: WorkspaceView.aceEditorHeight + "rem"
                    },
                    oncreate: function() {
                        WorkspaceView.editor = ace.edit("editor")
                        WorkspaceView.editor.getSession().setMode("ace/mode/javascript")
                        WorkspaceView.editor.getSession().setUseSoftTabs(true)
                    },
                    onupdate: function() {
                        WorkspaceView.editor.resize()
                    }
                }),
                m("div.w-90.ba.pa1.bg-light-gray", {
                    // splitter for resizing the editor's height
                    style: { cursor: "grab" },
                    draggable: true,
                    ondragstart: (event) => {
                        dragOriginY = event.screenY
                        event.dataTransfer.setData("Text", event.target.id)
                    },
                    ondragend: (event) => {
                        const yDifference = event.screenY - dragOriginY
                        const lineDifference = Math.floor(yDifference / WorkspaceView.editor.renderer.lineHeight)
                        WorkspaceView.aceEditorHeight = parseInt(WorkspaceView.aceEditorHeight) + lineDifference
                        if (WorkspaceView.aceEditorHeight < 5) { WorkspaceView.aceEditorHeight = 5 }
                        if (WorkspaceView.aceEditorHeight > 100) { WorkspaceView.aceEditorHeight = 100 }
                    },
                }),
                m("button.ma1", { onclick: WorkspaceView.doIt }, "Do it"),
                m("button.ma1", { onclick: WorkspaceView.printIt }, "Print it"),
                m("button.ma1", { onclick: WorkspaceView.inspectIt }, "Inspect it"),
                m("span.pa1"),
                m("button.ma1", { onclick: WorkspaceView.save }, "Save"),
                m("button.ma1", { onclick: WorkspaceView.clear }, "Clear"),
                m("button.ma1", { onclick: WorkspaceView.importText }, "Import"),
                m("button.ma1", { onclick: WorkspaceView.exportText }, "Export"),
                m("br"),
                m("button.ma1", { onclick: WorkspaceView.showExampleLog }, "Show example log"),
                m("button.ma1", { onclick: WorkspaceView.showLog }, "Show current log"),
                m("button.ma1", { onclick: WorkspaceView.loadLog }, "Load log"),
                m("span.pa1"),
                m("button.ma1", { onclick: WorkspaceView.previous }, "Previous"),
                m("button.ma1", { onclick: WorkspaceView.next }, "Next"),
            ])
        },
    }

    return WorkspaceView
})
<commit_msg>Add about link and supporting tech links<commit_after>define(["FileUtils", "EvalUtils", "MemoryArchive", "LocalStorageArchive", "ace/ace", "exampleLog"], function(
    FileUtils,
    EvalUtils,
    MemoryArchive,
    LocalStorageArchive,
    ace,
    exampleLog
) {
    "use strict"

    let Archive = LocalStorageArchive

    // Used for resizing the editor's height
    let dragOriginY

    // Convenience function which examples could use to put up closeable views
    window.show = function(viewFunction, extraStyling) {
        if (!extraStyling) { extraStyling = "" }
        let div = document.createElement("div")

        const ClosableComponent = {
            view() {
                return m("div.ba.ma3.pa3.bg-light-purple" + extraStyling,
                m("button.fr", {onclick: function () { document.body.removeChild(div) } }, "X"),
                viewFunction()
                )
            }
        }

        document.body.appendChild(div)
        m.mount(div, ClosableComponent)
    }

    const WorkspaceView = {
        editor: null,
        lastLoadedContents: "",
        currentItemIndex: null,
        archiveChoice: "local storage",
        savedItemIndexes: {
            "local storage": null,
            "memory": null,
        },
        aceEditorHeight: 20,
        toastMessages: [],

        oninit() {
            if (Archive.itemCount() === 0) {
                window.show(function () {
                    return m("div", `To get started with some example snippets,
                        click "Show example log", then "Load log", then "Next", and then "Do it".
                        Use "Previous" and "Next" to scroll through more example snippets.`)
                })
            }
        },

        changeArchive() {
            const oldChoice = WorkspaceView.archiveChoice
            const newChoice = (WorkspaceView.archiveChoice === "memory" ? "local storage": "memory")

            WorkspaceView.savedItemIndexes[oldChoice] = WorkspaceView.currentItemIndex
            WorkspaceView.archiveChoice = newChoice
            Archive = (newChoice === "memory" ? MemoryArchive : LocalStorageArchive)
            WorkspaceView.currentItemIndex = WorkspaceView.savedItemIndexes[newChoice]
            if (WorkspaceView.currentItemIndex === null) {
                WorkspaceView.setEditorContents("")
            } else {
                WorkspaceView.setEditorContents(Archive.getItem(WorkspaceView.currentItemIndex))
            }
        },

        setEditorContents(newContents, isNotSaved) {
            WorkspaceView.editor.setValue(newContents)
            if (!isNotSaved) {
                WorkspaceView.lastLoadedContents = newContents
            } else {
                WorkspaceView.currentItemIndex = null
            }
            WorkspaceView.editor.selection.clearSelection()
            WorkspaceView.editor.selection.moveCursorFileStart()
        },

        getEditorContents() {
            return WorkspaceView.editor.getValue()
        },

        getSelectedEditorText() {
            let selectedText = WorkspaceView.editor.session.getTextRange(WorkspaceView.editor.getSelectionRange())
            let isNoSelection = false
            if (!selectedText) {
                // assume user wants all text if nothing is selected
                selectedText = WorkspaceView.editor.getValue()
                isNoSelection = true
            }
            return {
                text: selectedText,
                isNoSelection,
            }
        },

        save() {
            const newContents = WorkspaceView.getEditorContents()
            Archive.addItem(newContents)
            WorkspaceView.lastLoadedContents = newContents
            WorkspaceView.currentItemIndex = Archive.itemCount() - 1
        },

        confirmClear(promptText) {
            if (!WorkspaceView.getEditorContents()) return true
            if (WorkspaceView.getEditorContents() === WorkspaceView.lastLoadedContents) return true
            if (!promptText) promptText = "You have unsaved editor changes. Proceed?"
            return confirm(promptText)
        },

        clear() {
            if (!WorkspaceView.confirmClear()) return
            WorkspaceView.setEditorContents("")
            WorkspaceView.currentItemIndex = null
        },

        doIt() {
            const selection = WorkspaceView.getSelectedEditorText()
            try {
                EvalUtils.eval(selection.text)
            } catch (error) {
                WorkspaceView.toast("Eval error:\n" + error)
            }
        },

        printIt() {
            const selection = WorkspaceView.getSelectedEditorText()
            const evalResult = "" + EvalUtils.evalOrError(selection.text)
            if (selection.isNoSelection) { WorkspaceView.editor.selection.moveCursorFileEnd() }
            const selectedRange = WorkspaceView.editor.selection.getRange()
            const start = selectedRange.end
            const end = WorkspaceView.editor.session.insert(start, evalResult)
            WorkspaceView.editor.selection.setRange({start, end})
            WorkspaceView.editor.focus()
        },

        inspectIt() {
            const selection = WorkspaceView.getSelectedEditorText()
            const evalResult = EvalUtils.evalOrError(selection.text)
            console.dir(evalResult)
        },

        importText() {
            if (!WorkspaceView.confirmClear()) return
            FileUtils.loadFromFile((fileName, fileContents) => {
                if (fileContents) {
                    const newContent = fileName + "\n---------------------------------------\n" + fileContents
                    WorkspaceView.setEditorContents(newContent)
                    m.redraw()
                }
            })
        },

        exportText() {
            const fileContents = WorkspaceView.getEditorContents()
            const provisionalFileName = fileContents.split("\n")[0]
            FileUtils.saveToFile(provisionalFileName, fileContents)
        },

        skip(offset) {
            if (!Archive.itemCount()) {
                WorkspaceView.toast("No log items to display. Try saving one first -- or show the example log in the editor and then load it.")
                return
            }
            if (Archive.itemCount() === 1) {
                WorkspaceView.toast("Only one log item to display. Try saving another one first.")
                return
            }
            if (!WorkspaceView.confirmClear()) return
            if (WorkspaceView.currentItemIndex === null) {
                WorkspaceView.currentItemIndex = (offset >= 0 ?  0 : Archive.itemCount() - 1)
            } else {
                WorkspaceView.currentItemIndex = (Archive.itemCount() + WorkspaceView.currentItemIndex + offset) % Archive.itemCount()
            }
            WorkspaceView.setEditorContents(Archive.getItem(WorkspaceView.currentItemIndex))
        },

        previous() { WorkspaceView.skip(-1) },

        next() { WorkspaceView.skip(1) },

        showLog() {
            if (!WorkspaceView.confirmClear()) return
            WorkspaceView.setEditorContents(Archive.textForLog())
            WorkspaceView.currentItemIndex = null
        },

        loadLog() {
            if (Archive.itemCount() && !confirm("Replace all items with entered text for a log?")) return
            try {
                Archive.loadFromLogText(WorkspaceView.getEditorContents())
            } catch (error) {
                WorkspaceView.toast("Problem loading log from editor:\n" + error)
                return
            }
            WorkspaceView.currentItemIndex = null
            // Update lastLoadedContents in case pasted in contents to avoid warning later since data was processed as intended
            WorkspaceView.lastLoadedContents = WorkspaceView.getEditorContents()
            WorkspaceView.toast("Loaded log from editor")
        },

        showExampleLog() {
            if (!WorkspaceView.confirmClear()) return
            WorkspaceView.setEditorContents(JSON.stringify(exampleLog, null, 4))
        },

        toast(message, delay) {
            function removeToastAfterDelay() {
                setTimeout(function() {
                    WorkspaceView.toastMessages.shift()
                    if ( WorkspaceView.toastMessages.length ) { removeToastAfterDelay() }
                    m.redraw()
                }, WorkspaceView.toastMessages[0].delay)
            }
            if (delay === undefined) { delay = 3000 }
            WorkspaceView.toastMessages.push({message, delay})
            if ( WorkspaceView.toastMessages.length === 1) { removeToastAfterDelay() }
        },

        view() {
            return m("main.ma2", [
                m("div#toastDiv.fixed.top-2.left-2.w-40.pa2.fieldset.bg-gold.pl3.pr3.tc.o-90.z-max",
                    { hidden: WorkspaceView.toastMessages.length === 0 },
                    WorkspaceView.toastMessages.length ? WorkspaceView.toastMessages[0].message : ""
                ),
                m("button", {onclick: WorkspaceView.changeArchive}, "Archive: " + WorkspaceView.archiveChoice),
                m("a.ml2", { target: "_blank", href: "https://github.com/pdfernhout/Twirlip7" }, "About Twirlip"),
                m("span.ml2", "which uses:"),
                m("a.ml2", { target: "_blank", href: "https://mithril.js.org/" }, "Mithril.js"),
                m("a.ml2", { target: "_blank", href: "http://tachyons.io/" }, "Tachyons.js"),
                m("a.ml2", { target: "_blank", href: "https://developer.mozilla.org/en-US/docs/Web/JavaScript" }, "JavaScript"),
                m("h4.bw24.b--solid.b--blue.pa1",
                    "JavaScript snippet ",
                    (WorkspaceView.currentItemIndex === null ? "???" : WorkspaceView.currentItemIndex + 1),
                    " of ",
                    Archive.itemCount()
                ),
                m("input#fileInput", { "type" : "file" , "hidden" : true } ),
                m("div.w-90#editor", {
                    style: {
                        height: WorkspaceView.aceEditorHeight + "rem"
                    },
                    oncreate: function() {
                        WorkspaceView.editor = ace.edit("editor")
                        WorkspaceView.editor.getSession().setMode("ace/mode/javascript")
                        WorkspaceView.editor.getSession().setUseSoftTabs(true)
                    },
                    onupdate: function() {
                        WorkspaceView.editor.resize()
                    }
                }),
                m("div.w-90.ba.pa1.bg-light-gray", {
                    // splitter for resizing the editor's height
                    style: { cursor: "grab" },
                    draggable: true,
                    ondragstart: (event) => {
                        dragOriginY = event.screenY
                        event.dataTransfer.setData("Text", event.target.id)
                    },
                    ondragend: (event) => {
                        const yDifference = event.screenY - dragOriginY
                        const lineDifference = Math.floor(yDifference / WorkspaceView.editor.renderer.lineHeight)
                        WorkspaceView.aceEditorHeight = parseInt(WorkspaceView.aceEditorHeight) + lineDifference
                        if (WorkspaceView.aceEditorHeight < 5) { WorkspaceView.aceEditorHeight = 5 }
                        if (WorkspaceView.aceEditorHeight > 100) { WorkspaceView.aceEditorHeight = 100 }
                    },
                }),
                m("button.ma1", { onclick: WorkspaceView.doIt }, "Do it"),
                m("button.ma1", { onclick: WorkspaceView.printIt }, "Print it"),
                m("button.ma1", { onclick: WorkspaceView.inspectIt }, "Inspect it"),
                m("span.pa1"),
                m("button.ma1", { onclick: WorkspaceView.save }, "Save"),
                m("button.ma1", { onclick: WorkspaceView.clear }, "Clear"),
                m("button.ma1", { onclick: WorkspaceView.importText }, "Import"),
                m("button.ma1", { onclick: WorkspaceView.exportText }, "Export"),
                m("br"),
                m("button.ma1", { onclick: WorkspaceView.showExampleLog }, "Show example log"),
                m("button.ma1", { onclick: WorkspaceView.showLog }, "Show current log"),
                m("button.ma1", { onclick: WorkspaceView.loadLog }, "Load log"),
                m("span.pa1"),
                m("button.ma1", { onclick: WorkspaceView.previous }, "Previous"),
                m("button.ma1", { onclick: WorkspaceView.next }, "Next"),
            ])
        },
    }

    return WorkspaceView
})
<|endoftext|>


<commit_before>/*global define, Raphael*/
'use strict';
define(['jquery', 'underscore', 'backbone', 'helpers/raphael_support', 'templates', 'bootstrap', 'views/osd-detail-view', 'models/application-model', 'raphael', 'marionette'], function($, _, Backbone, Rs, JST, bs, View, Models) {
    var OSDVisualization = Backbone.Marionette.ItemView.extend({
        template: JST['app/scripts/templates/viz.ejs'],
        serializeData: function() {
            return {};
        },
        originX: 0,
        originY: 0,
        step: 40,
        timer: null,
        ui: {
            viz: '.viz',
            detail: '.detail tbody',
            spinner: '.icon-spinner'
        },
        events: {
            'click .viz': 'clickHandler'
        },
        collectionEvents: {
            'add': 'addOSD',
            'remove': 'removeOSD',
            'change': 'updateOSD',
        },
        initialize: function(options) {
            this.App = options.App;
            this.width = 17 * this.step;
            this.height = 11 * this.step;
            this.w = 720;
            this.h = 520;
            _.bindAll(this);
            this.keyHandler = _.debounce(this.keyHandler, 250, true);
            this.listenTo(this.App.vent, 'keyup', this.keyHandler);
            this.listenTo(this.collection, 'request', this.spinnerShow);
            this.listenTo(this.collection, 'sync error', this.spinnerHide);
        },
        spinnerShow: function() {
            this.ui.spinner.show();
        },
        spinnerHide: function() {
            this.ui.spinner.hide();
        },
        addOSD: function(m) {
            this.moveCircle(m);
        },
        removeOSD: function(m) {
            this.collection.remove(m);
            if (m.views) {
                var circle = m.views.circle;
                circle.animate({
                    'opacity': 0,
                    'r': 0
                }, 250, 'easeIn', function() {
                    circle.remove();
                });
                m.views.text.remove();
                m.views = null;
            }
        },
        updateOSD: function(m) {
            m.set(m.attributes);
        },
        drawGrid: function(d) {
            var path = Rs.calcGrid(this.originX, this.originY, this.width, this.height, this.step);
            var path1 = this.r.path('M0,0').attr({
                'stroke-width': 1,
                'stroke': '#5e6a71',
                'opacity': 0.40
            });
            this.drawLegend(this.r, 285, 475);
            var anim = Raphael.animation({
                path: path,
                callback: d.resolve
            }, 250);
            path1.animate(anim);
        },
        startPosition: [{
            x: 40,
            y: 40
        }, {
            x: 600,
            y: 40
        }, {
            x: 600,
            y: 400
        }, {
            x: 40,
            y: 400
        }, ],
        moveCircle: function(m) {
            var start = this.startPosition[Math.floor(Math.random() * 4)];
            var pos = Rs.calcPosition(m.get('osd'), this.originX, this.originY, this.width, this.height, this.step);
            this.animateCircleTraversal(this.r, start.x, start.y, 8, pos.nx, pos.ny, m);
        },
        calculatePositions: function() {
            this.collection.each(this.moveCircle);
            return $.Deferred().resolve();
        },
        legendCircle: function(r, originX, originY, percent) {
            // Helper method to draw circles for use as legends beneath viz.
            var srctext = ['down', 'up/out', 'up/in'];
            var srcstate = [{
                up: 0,
                'in': 0
            }, {
                up: 1,
                'in': 0
            }, {
                up: 1,
                'in': 1
            }];
            var i = Math.round(1 / percent) - 1;
            var m = new Models.OSDModel(_.extend(srcstate[i], {
                capacity: 1024,
                used: percent * 1024
            }));
            var c = r.circle(originX, originY, 16 * m.getPercentage()).attr({
                fill: m.getColor(),
                stroke: 'none',
                'cursor': 'default',
                opacity: 0
            });
            var aFn = Raphael.animation({
                opacity: 1
            }, 250, 'easeOut');
            var text = srctext[i];
            r.text(originX, originY + 23, text).attr({
                'cursor': 'default',
                'font-size': '12px',
                'font-family': 'ApexSansLight'
            });
            return c.animate(aFn);
        },
        drawLegend: function(r, originX, originY) {
            // Calls legend circle to place in viz.
            var xp = originX,
                i;
            for (i = 1; i <= 3; i += 1, xp += 50) {
                this.legendCircle(r, xp, originY, i / 3);
            }
        },
        animateCircleTraversal: function(r, originX, originY, radius, destX, destY, model) {
            var c = r.circle(originX, originY, 20 * model.getPercentage()).attr({
                fill: model.getColor(),
                stroke: 'none'
            });
            c.data('modelid', model.cid);
            var t;
            var aFn = Raphael.animation({
                cx: destX,
                cy: originY
            }, 250, 'easeOut', function() {
                c.animate({
                    cx: destX,
                    cy: destY
                }, 333, 'easeIn', function() {
                    t = r.text(destX, destY - 1, model.get('osd')).attr({
                        font: '',
                        stroke: '',
                        fill: '',
                        style: ''
                    });
                    t.data('modelid', model.cid);
                    model.views = {
                        circle: c,
                        text: t
                    };
                });
            });
            return c.animate(aFn);
        },
        simulateUsedChanges: function() {
            var maxRed = 2;
            this.collection.each(function(m) {
                var up = true;
                var _in = Math.random();
                _in = _in < 0.95;
                if (!_in && (Math.random() > 0.6) && maxRed > 0) {
                    maxRed -= 1;
                    up = false;
                    //console.log(m.cid + ' setting to down');
                }
                m.set({
                    'up': up,
                    'in': _in
                });
            });
            this.App.vent.trigger('updateTotals');
            this.App.vent.trigger('status:healthwarn');
        },
        resetChanges: function() {
            this.collection.each(function(m) {
                m.set({
                    'up': true,
                    'in': true
                });
            });
            this.App.vent.trigger('updateTotals');
            this.App.vent.trigger('status:healthok');
        },
        startSimulation: function() {
            var self = this;
            this.timer = setTimeout(function() {
                self.simulateUsedChanges();
                self.timer = self.startSimulation();
            }, 3000);
            return this.timer;
        },
        stopSimulation: function() {
            clearTimeout(this.timer);
            this.timer = null;
        },
        render: function() {
            Backbone.Marionette.ItemView.prototype.render.apply(this);
            this.r = window.Raphael(this.ui.viz[0], this.w, this.h);
            this.detailPanel = new View({
                App: this.App,
                el: this.ui.detail
            });
            var d = $.Deferred();
            $.when(this.drawGrid(d));
            var p = d.promise();
            p.then(this.calculatePositions);
            return p;
        },
        keyHandler: function(evt) {
            evt.preventDefault();
            if (!evt.keyCode) {
                return;
            }
            var keyCode = evt.keyCode;
            if (keyCode === 82) {
                this.resetChanges();
                return;
            }
            if (keyCode === 85) {
                this.simulateUsedChanges();
                return;
            }
            if (keyCode === 32) {
                var $spinner = $('.icon-spinner');
                if (this.timer === null) {
                    this.startSimulation();
                    $spinner.closest('i').addClass('.icon-spin').show();
                } else {
                    this.stopSimulation();
                    $spinner.closest('i').removeClass('.icon-spin').hide();
                }
            }
        },
        clickHandler: function(evt) {
            if (evt.target.nodeName === 'tspan' || evt.target.nodeName === 'circle') {
                var x = evt.clientX;
                var y = evt.clientY;
                //console.log(x + ' / ' + y);
                var el = this.r.getElementByPoint(x, y);
                //console.log(el);
                if (el) {
                    var cid = el.data('modelid');
                    //console.log(cid);
                    if (cid) {
                        // ignore circles and tspans without data
                        this.detailPanel.model.set(this.collection.get(cid).attributes);
                    }
                    return;
                }
            }
        }
    });
    return OSDVisualization;
});
<commit_msg>Handle reset events on collection.<commit_after>/*global define, Raphael*/
'use strict';
define(['jquery', 'underscore', 'backbone', 'helpers/raphael_support', 'templates', 'bootstrap', 'views/osd-detail-view', 'models/application-model', 'raphael', 'marionette'], function($, _, Backbone, Rs, JST, bs, View, Models) {
    var OSDVisualization = Backbone.Marionette.ItemView.extend({
        template: JST['app/scripts/templates/viz.ejs'],
        serializeData: function() {
            return {};
        },
        originX: 0,
        originY: 0,
        step: 40,
        timer: null,
        ui: {
            viz: '.viz',
            detail: '.detail tbody',
            spinner: '.icon-spinner'
        },
        events: {
            'click .viz': 'clickHandler'
        },
        collectionEvents: {
            'add': 'addOSD',
            'remove': 'removeOSD',
            'change': 'updateOSD',
        },
        initialize: function(options) {
            this.App = options.App;
            this.width = 17 * this.step;
            this.height = 11 * this.step;
            this.w = 720;
            this.h = 520;
            _.bindAll(this);
            this.keyHandler = _.debounce(this.keyHandler, 250, true);
            this.listenTo(this.App.vent, 'keyup', this.keyHandler);
            this.listenTo(this.collection, 'request', this.spinnerShow);
            this.listenTo(this.collection, 'sync error', this.spinnerHide);
            this.listenTo(this.collection, 'reset', this.resetViews);
        },
        resetViews: function(collection, options) {
            _.each(options.previousModels, this.cleanupModelView);
        },
        spinnerShow: function() {
            this.ui.spinner.show();
        },
        spinnerHide: function() {
            this.ui.spinner.hide();
        },
        addOSD: function(m) {
            this.moveCircle(m);
        },
        cleanupModelView: function(m) {
            if (m.views) {
                var circle = m.views.circle;
                circle.animate({
                    'opacity': 0,
                    'r': 0
                }, 250, 'easeIn', function() {
                    circle.remove();
                });
                m.views.text.remove();
                m.views = null;
            }
        },
        removeOSD: function(m) {
            this.collection.remove(m);
            this.cleanupModelView(m);
        },
        updateOSD: function(m) {
            m.set(m.attributes);
        },
        drawGrid: function(d) {
            var path = Rs.calcGrid(this.originX, this.originY, this.width, this.height, this.step);
            var path1 = this.r.path('M0,0').attr({
                'stroke-width': 1,
                'stroke': '#5e6a71',
                'opacity': 0.40
            });
            this.drawLegend(this.r, 285, 475);
            var anim = Raphael.animation({
                path: path,
                callback: d.resolve
            }, 250);
            path1.animate(anim);
        },
        startPosition: [{
            x: 40,
            y: 40
        }, {
            x: 600,
            y: 40
        }, {
            x: 600,
            y: 400
        }, {
            x: 40,
            y: 400
        }, ],
        moveCircle: function(m) {
            var start = this.startPosition[Math.floor(Math.random() * 4)];
            var pos = Rs.calcPosition(m.get('osd'), this.originX, this.originY, this.width, this.height, this.step);
            this.animateCircleTraversal(this.r, start.x, start.y, 8, pos.nx, pos.ny, m);
        },
        calculatePositions: function() {
            this.collection.each(this.moveCircle);
            return $.Deferred().resolve();
        },
        legendCircle: function(r, originX, originY, percent) {
            // Helper method to draw circles for use as legends beneath viz.
            var srctext = ['down', 'up/out', 'up/in'];
            var srcstate = [{
                up: 0,
                'in': 0
            }, {
                up: 1,
                'in': 0
            }, {
                up: 1,
                'in': 1
            }];
            var i = Math.round(1 / percent) - 1;
            var m = new Models.OSDModel(_.extend(srcstate[i], {
                capacity: 1024,
                used: percent * 1024
            }));
            var c = r.circle(originX, originY, 16 * m.getPercentage()).attr({
                fill: m.getColor(),
                stroke: 'none',
                'cursor': 'default',
                opacity: 0
            });
            var aFn = Raphael.animation({
                opacity: 1
            }, 250, 'easeOut');
            var text = srctext[i];
            r.text(originX, originY + 23, text).attr({
                'cursor': 'default',
                'font-size': '12px',
                'font-family': 'ApexSansLight'
            });
            return c.animate(aFn);
        },
        drawLegend: function(r, originX, originY) {
            // Calls legend circle to place in viz.
            var xp = originX,
                i;
            for (i = 1; i <= 3; i += 1, xp += 50) {
                this.legendCircle(r, xp, originY, i / 3);
            }
        },
        animateCircleTraversal: function(r, originX, originY, radius, destX, destY, model) {
            var c = r.circle(originX, originY, 20 * model.getPercentage()).attr({
                fill: model.getColor(),
                stroke: 'none'
            });
            c.data('modelid', model.cid);
            var t;
            var aFn = Raphael.animation({
                cx: destX,
                cy: originY
            }, 250, 'easeOut', function() {
                c.animate({
                    cx: destX,
                    cy: destY
                }, 333, 'easeIn', function() {
                    t = r.text(destX, destY - 1, model.get('osd')).attr({
                        font: '',
                        stroke: '',
                        fill: '',
                        style: ''
                    });
                    t.data('modelid', model.cid);
                    model.views = {
                        circle: c,
                        text: t
                    };
                });
            });
            return c.animate(aFn);
        },
        simulateUsedChanges: function() {
            var maxRed = 2;
            this.collection.each(function(m) {
                var up = true;
                var _in = Math.random();
                _in = _in < 0.95;
                if (!_in && (Math.random() > 0.6) && maxRed > 0) {
                    maxRed -= 1;
                    up = false;
                    //console.log(m.cid + ' setting to down');
                }
                m.set({
                    'up': up,
                    'in': _in
                });
            });
            this.App.vent.trigger('updateTotals');
            this.App.vent.trigger('status:healthwarn');
        },
        resetChanges: function() {
            this.collection.each(function(m) {
                m.set({
                    'up': true,
                    'in': true
                });
            });
            this.App.vent.trigger('updateTotals');
            this.App.vent.trigger('status:healthok');
        },
        startSimulation: function() {
            var self = this;
            this.timer = setTimeout(function() {
                self.simulateUsedChanges();
                self.timer = self.startSimulation();
            }, 3000);
            return this.timer;
        },
        stopSimulation: function() {
            clearTimeout(this.timer);
            this.timer = null;
        },
        render: function() {
            Backbone.Marionette.ItemView.prototype.render.apply(this);
            this.r = window.Raphael(this.ui.viz[0], this.w, this.h);
            this.detailPanel = new View({
                App: this.App,
                el: this.ui.detail
            });
            var d = $.Deferred();
            $.when(this.drawGrid(d));
            var p = d.promise();
            p.then(this.calculatePositions);
            return p;
        },
        keyHandler: function(evt) {
            evt.preventDefault();
            if (!evt.keyCode) {
                return;
            }
            var keyCode = evt.keyCode;
            if (keyCode === 82) {
                this.resetChanges();
                return;
            }
            if (keyCode === 85) {
                this.simulateUsedChanges();
                return;
            }
            if (keyCode === 32) {
                var $spinner = $('.icon-spinner');
                if (this.timer === null) {
                    this.startSimulation();
                    $spinner.closest('i').addClass('.icon-spin').show();
                } else {
                    this.stopSimulation();
                    $spinner.closest('i').removeClass('.icon-spin').hide();
                }
            }
        },
        clickHandler: function(evt) {
            if (evt.target.nodeName === 'tspan' || evt.target.nodeName === 'circle') {
                var x = evt.clientX;
                var y = evt.clientY;
                //console.log(x + ' / ' + y);
                var el = this.r.getElementByPoint(x, y);
                //console.log(el);
                if (el) {
                    var cid = el.data('modelid');
                    //console.log(cid);
                    if (cid) {
                        // ignore circles and tspans without data
                        this.detailPanel.model.set(this.collection.get(cid).attributes);
                    }
                    return;
                }
            }
        }
    });
    return OSDVisualization;
});
<|endoftext|>


<commit_before>define([
    'uijet_dir/uijet',
    'browser/app'
], function (uijet) {

    var BASE_API_URL = 'http://api.obudget.dev:8000/',
        BudgetModel = uijet.Model({
            idAttribute : 'uuid'
        }),
        BudgetsCollection = uijet.Collection({
            model   : BudgetModel,
            url     : BASE_API_URL + 'budgets/',
            parse   : function (response) {
                return response.results;
            }
        }),
        exports = {
            budget_model        : BudgetModel,
            budgets_collection  : BudgetsCollection
        };

    uijet.Resource('Budgets', BudgetsCollection);

    return exports;
});<commit_msg>Added chart data handling<commit_after>define([
    'uijet_dir/uijet',
    'browser/app'
], function (uijet) {

    var BASE_API_URL = 'http://api.obudget.dev:8000/',
        BudgetModel = uijet.Model({
            idAttribute : 'uuid'
        }),
        BudgetsCollection = uijet.Collection({
            model       : BudgetModel,
            url         : BASE_API_URL + 'budgets/',
            parse       : function (response) {
                return response.results;
            },
            chartData   : function (index) {
                var budgets = this.toJSON(),
                    data = [];

                budgets.forEach(function (budget) {
                    var time = new Date(budget.period_start).getFullYear();
                    data.push({
                        x : time,
                        y : budget.items[index].amount
                    });
                });

                return data.sort(function (a, b) {
                    return a.x - b.x;
                });
            }
        }),
        exports = {
            budget_model        : BudgetModel,
            budgets_collection  : BudgetsCollection
        };

    uijet.Resource('Budgets', BudgetsCollection);

    return exports;
});<|endoftext|>


<commit_before>/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/toMathML.js
 *
 *  Implements a toMathML() method for the mml Element Jax that returns
 *  a MathML string from a given math expression.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2010-2014 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

MathJax.Hub.Register.LoadHook("[MathJax]/jax/element/mml/jax.js",function () {
  var VERSION = "2.5.0-beta";

  var MML = MathJax.ElementJax.mml
      SETTINGS = MathJax.Hub.config.menuSettings;

  MML.mbase.Augment({

    toMathML: function (space) {
      var inferred = (this.inferred && this.parent.inferRow);
      if (space == null) {space = ""}
      var tag = this.type, attr = this.toMathMLattributes();
      if (tag === "mspace") {return space + "<"+tag+attr+" />"}
      var data = [], SPACE = (this.isToken ? "" : space+(inferred ? "" : "  "));
      for (var i = 0, m = this.data.length; i < m; i++) {
        if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))}
          else if (!this.isToken && !this.isChars) {data.push(SPACE+"<mrow />")}
      }
      if (this.isToken) {return space + "<"+tag+attr+">"+data.join("")+"</"+tag+">"}
      if (inferred) {return data.join("\n")}
      if (data.length === 0 || (data.length === 1 && data[0] === ""))
        {return space + "<"+tag+attr+" />"}
      return space + "<"+tag+attr+">\n"+data.join("\n")+"\n"+ space +"</"+tag+">";
    },

    toMathMLattributes: function () {
      var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults);
      var names = (this.attrNames||MML.copyAttributeNames),
          skip = MML.skipAttributes, copy = MML.copyAttributes;
      var attr = [];

      if (this.type === "math" && (!this.attr || !this.attr.xmlns))
        {attr.push('xmlns="http://www.w3.org/1998/Math/MathML"')}
      if (!this.attrNames) {
        for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) {
          if (this[id] != null && this[id] !== defaults[id]) {
            if (this.Get(id,null,1) !== this[id])
              attr.push(id+'="'+this.toMathMLattribute(this[id])+'"');
          }
        }}
      }
      for (var i = 0, m = names.length; i < m; i++) {
        if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue;
        value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]}
        if (value != null) {attr.push(names[i]+'="'+this.toMathMLquote(value)+'"')}
      }
      this.toMathMLclass(attr);
      if (attr.length) {return " "+attr.join(" ")} else {return ""}
    },
    toMathMLclass: function (attr) {
      var CLASS = []; if (this["class"]) {CLASS.push(this["class"])}
      if (this.isa(MML.TeXAtom) && SETTINGS.texHints) {
        var TEXCLASS = ["ORD","OP","BIN","REL","OPEN","CLOSE","PUNCT","INNER","VCENTER"][this.texClass];
        if (TEXCLASS) {CLASS.push("MJX-TeXAtom-"+TEXCLASS)}
      }
      if (this.mathvariant && this.toMathMLvariants[this.mathvariant])
        {CLASS.push("MJX"+this.mathvariant)}
      if (this.variantForm) {CLASS.push("MJX-variant")}
      if (CLASS.length) {attr.unshift('class="'+CLASS.join(" ")+'"')}
    },
    toMathMLattribute: function (value) {
      if (typeof(value) === "string" &&
          value.replace(/ /g,"").match(/^(([-+])?(\d+(\.\d*)?|\.\d+))mu$/)) {
        // FIXME:  should take scriptlevel into account
        return (RegExp.$2||"")+((1/18)*RegExp.$3).toFixed(3).replace(/\.?0+$/,"")+"em";
      }
      else if (this.toMathMLvariants[value]) {return this.toMathMLvariants[value]}
      return this.toMathMLquote(value);
    },
    toMathMLvariants: {
      "-tex-caligraphic":      MML.VARIANT.SCRIPT,
      "-tex-caligraphic-bold": MML.VARIANT.BOLDSCRIPT,
      "-tex-oldstyle":         MML.VARIANT.NORMAL,
      "-tex-oldstyle-bold":    MML.VARIANT.BOLD,
      "-tex-mathit":           MML.VARIANT.ITALIC
    },

    toMathMLquote: function (string) {
      string = String(string).split("");
      for (var i = 0, m = string.length; i < m; i++) {
        var n = string[i].charCodeAt(0);
        if (n <= 0xD7FF || 0xE000 <= n) {
          // Code points U+0000 to U+D7FF and U+E000 to U+FFFF.
          // They are directly represented by n.
          if (n < 0x20 || n > 0x7E) {
            string[i] = "&#x"+n.toString(16).toUpperCase()+";";
          } else {
            var c =
              {'&':'&', '<':'<', '>':'>', '"':'"'}[string[i]];
            if (c) {string[i] = c}
          }
        } else if (i+1 < m) {
          // Code points U+10000 to U+10FFFF.
          // n is the lead surrogate, let's read the trail surrogate.
          var trailSurrogate = string[i+1].charCodeAt(0);
          var codePoint = (((n-0xD800)<<10)+(trailSurrogate-0xDC00)+0x10000);
          string[i] = "&#x"+codePoint.toString(16).toUpperCase()+";";
          string[i+1] = "";
          i++;
        } else {
          // n is a lead surrogate without corresponding trail surrogate:
          // remove that character.
          string[i] = "";
        }
      }
      return string.join("");
    }
  });

  //
  //  Override math.toMathML in order to add semantics tag
  //  for the input format, if the user requests that in the
  //  Show As menu.
  //
  MML.math.Augment({
    toMathML: function (space,jax) {
      var annotation;
      if (space == null) {space = ""}
      if (jax && jax.originalText && SETTINGS.semantics)
        {annotation = MathJax.InputJax[jax.inputJax].annotationEncoding}
      var nested = (this.data[0] && this.data[0].data.length > 1);
      var tag = this.type, attr = this.toMathMLattributes();
      var data = [], SPACE = space + (annotation ? "    " : "") + (nested ? "  " : "");
      for (var i = 0, m = this.data.length; i < m; i++) {
        if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))}
          else {data.push(SPACE+"<mrow />")}
      }
      if (data.length === 0 || (data.length === 1 && data[0] === "")) {
        if (!annotation) {return "<"+tag+attr+" />"}
        data.push(SPACE+"<mrow />");
      }
      if (annotation) {
        if (nested) {data.unshift(space+"    <mrow>"); data.push(space+"    </mrow>")}
        data.unshift(space+"  <semantics>");
        var xmlEscapedTex = jax.originalText.replace(/[&<>]/g, function(item) {
            return { '>': '>', '<': '<','&': '&' }[item]
        });
      data.push(space+'    <annotation encoding="'+annotation+'">'+xmlEscapedTex+"</annotation>");
        data.push(space+"  </semantics>");
      }
      return space+"<"+tag+attr+">\n"+data.join("\n")+"\n"+space+"</"+tag+">";
    }
  });

  MML.msubsup.Augment({
    toMathML: function (space) {
      var tag = this.type;
      if (this.data[this.sup] == null) {tag = "msub"}
      if (this.data[this.sub] == null) {tag = "msup"}
      var attr = this.toMathMLattributes();
      delete this.data[0].inferred;
      var data = [];
      for (var i = 0, m = this.data.length; i < m; i++)
        {if (this.data[i]) {data.push(this.data[i].toMathML(space+"  "))}}
      return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">";
    }
  });

  MML.munderover.Augment({
    toMathML: function (space) {
      var tag = this.type;
      if (this.data[this.under] == null) {tag = "mover"}
      if (this.data[this.over] == null)  {tag = "munder"}
      var attr = this.toMathMLattributes();
      delete this.data[0].inferred;
      var data = [];
      for (var i = 0, m = this.data.length; i < m; i++)
        {if (this.data[i]) {data.push(this.data[i].toMathML(space+"  "))}}
      return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">";
    }
  });

  MML.TeXAtom.Augment({
    toMathML: function (space) {
      // FIXME:  Handle spacing using mpadded?
      var attr = this.toMathMLattributes();
      if (!attr && this.data[0].data.length === 1) {return space.substr(2) + this.data[0].toMathML(space)}
      return space+"<mrow"+attr+">\n" + this.data[0].toMathML(space+"  ")+"\n"+space+"</mrow>";
    }
  });

  MML.chars.Augment({
    toMathML: function (space) {return (space||"") + this.toMathMLquote(this.toString())}
  });

  MML.entity.Augment({
    toMathML: function (space) {return (space||"") + "&"+this.data[0]+";<!-- "+this.toString()+" -->"}
  });

  MML.xml.Augment({
   toMathML: function (space) {return (space||"") + this.toString()}
  });

  MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () {
    MML.TeXmathchoice.Augment({
      toMathML: function (space) {return this.Core().toMathML(space)}
    });
  });

  MathJax.Hub.Startup.signal.Post("toMathML Ready");

});

MathJax.Ajax.loadComplete("[MathJax]/extensions/toMathML.js");
<commit_msg>Fix indenting of toMathML output.<commit_after>/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/toMathML.js
 *
 *  Implements a toMathML() method for the mml Element Jax that returns
 *  a MathML string from a given math expression.
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2010-2014 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

MathJax.Hub.Register.LoadHook("[MathJax]/jax/element/mml/jax.js",function () {
  var VERSION = "2.5.0-beta";

  var MML = MathJax.ElementJax.mml
      SETTINGS = MathJax.Hub.config.menuSettings;

  MML.mbase.Augment({

    toMathML: function (space) {
      var inferred = (this.inferred && this.parent.inferRow);
      if (space == null) {space = ""}
      var tag = this.type, attr = this.toMathMLattributes();
      if (tag === "mspace") {return space + "<"+tag+attr+" />"}
      var data = [], SPACE = (this.isToken ? "" : space+(inferred ? "" : "  "));
      for (var i = 0, m = this.data.length; i < m; i++) {
        if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))}
          else if (!this.isToken && !this.isChars) {data.push(SPACE+"<mrow />")}
      }
      if (this.isToken) {return space + "<"+tag+attr+">"+data.join("")+"</"+tag+">"}
      if (inferred) {return data.join("\n")}
      if (data.length === 0 || (data.length === 1 && data[0] === ""))
        {return space + "<"+tag+attr+" />"}
      return space + "<"+tag+attr+">\n"+data.join("\n")+"\n"+ space +"</"+tag+">";
    },

    toMathMLattributes: function () {
      var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults);
      var names = (this.attrNames||MML.copyAttributeNames),
          skip = MML.skipAttributes, copy = MML.copyAttributes;
      var attr = [];

      if (this.type === "math" && (!this.attr || !this.attr.xmlns))
        {attr.push('xmlns="http://www.w3.org/1998/Math/MathML"')}
      if (!this.attrNames) {
        for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) {
          if (this[id] != null && this[id] !== defaults[id]) {
            if (this.Get(id,null,1) !== this[id])
              attr.push(id+'="'+this.toMathMLattribute(this[id])+'"');
          }
        }}
      }
      for (var i = 0, m = names.length; i < m; i++) {
        if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue;
        value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]}
        if (value != null) {attr.push(names[i]+'="'+this.toMathMLquote(value)+'"')}
      }
      this.toMathMLclass(attr);
      if (attr.length) {return " "+attr.join(" ")} else {return ""}
    },
    toMathMLclass: function (attr) {
      var CLASS = []; if (this["class"]) {CLASS.push(this["class"])}
      if (this.isa(MML.TeXAtom) && SETTINGS.texHints) {
        var TEXCLASS = ["ORD","OP","BIN","REL","OPEN","CLOSE","PUNCT","INNER","VCENTER"][this.texClass];
        if (TEXCLASS) {CLASS.push("MJX-TeXAtom-"+TEXCLASS)}
      }
      if (this.mathvariant && this.toMathMLvariants[this.mathvariant])
        {CLASS.push("MJX"+this.mathvariant)}
      if (this.variantForm) {CLASS.push("MJX-variant")}
      if (CLASS.length) {attr.unshift('class="'+CLASS.join(" ")+'"')}
    },
    toMathMLattribute: function (value) {
      if (typeof(value) === "string" &&
          value.replace(/ /g,"").match(/^(([-+])?(\d+(\.\d*)?|\.\d+))mu$/)) {
        // FIXME:  should take scriptlevel into account
        return (RegExp.$2||"")+((1/18)*RegExp.$3).toFixed(3).replace(/\.?0+$/,"")+"em";
      }
      else if (this.toMathMLvariants[value]) {return this.toMathMLvariants[value]}
      return this.toMathMLquote(value);
    },
    toMathMLvariants: {
      "-tex-caligraphic":      MML.VARIANT.SCRIPT,
      "-tex-caligraphic-bold": MML.VARIANT.BOLDSCRIPT,
      "-tex-oldstyle":         MML.VARIANT.NORMAL,
      "-tex-oldstyle-bold":    MML.VARIANT.BOLD,
      "-tex-mathit":           MML.VARIANT.ITALIC
    },

    toMathMLquote: function (string) {
      string = String(string).split("");
      for (var i = 0, m = string.length; i < m; i++) {
        var n = string[i].charCodeAt(0);
        if (n <= 0xD7FF || 0xE000 <= n) {
          // Code points U+0000 to U+D7FF and U+E000 to U+FFFF.
          // They are directly represented by n.
          if (n < 0x20 || n > 0x7E) {
            string[i] = "&#x"+n.toString(16).toUpperCase()+";";
          } else {
            var c =
              {'&':'&', '<':'<', '>':'>', '"':'"'}[string[i]];
            if (c) {string[i] = c}
          }
        } else if (i+1 < m) {
          // Code points U+10000 to U+10FFFF.
          // n is the lead surrogate, let's read the trail surrogate.
          var trailSurrogate = string[i+1].charCodeAt(0);
          var codePoint = (((n-0xD800)<<10)+(trailSurrogate-0xDC00)+0x10000);
          string[i] = "&#x"+codePoint.toString(16).toUpperCase()+";";
          string[i+1] = "";
          i++;
        } else {
          // n is a lead surrogate without corresponding trail surrogate:
          // remove that character.
          string[i] = "";
        }
      }
      return string.join("");
    }
  });

  //
  //  Override math.toMathML in order to add semantics tag
  //  for the input format, if the user requests that in the
  //  Show As menu.
  //
  MML.math.Augment({
    toMathML: function (space,jax) {
      var annotation;
      if (space == null) {space = ""}
      if (jax && jax.originalText && SETTINGS.semantics)
        {annotation = MathJax.InputJax[jax.inputJax].annotationEncoding}
      var nested = (this.data[0] && this.data[0].data.length > 1);
      var tag = this.type, attr = this.toMathMLattributes();
      var data = [], SPACE = space + (annotation ? "  " + (nested ? "  " : "") : "") + "  ";
      for (var i = 0, m = this.data.length; i < m; i++) {
        if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))}
          else {data.push(SPACE+"<mrow />")}
      }
      if (data.length === 0 || (data.length === 1 && data[0] === "")) {
        if (!annotation) {return "<"+tag+attr+" />"}
        data.push(SPACE+"<mrow />");
      }
      if (annotation) {
        if (nested) {data.unshift(space+"    <mrow>"); data.push(space+"    </mrow>")}
        data.unshift(space+"  <semantics>");
        var xmlEscapedTex = jax.originalText.replace(/[&<>]/g, function(item) {
            return { '>': '>', '<': '<','&': '&' }[item]
        });
        data.push(space+'    <annotation encoding="'+annotation+'">'+xmlEscapedTex+"</annotation>");
        data.push(space+"  </semantics>");
      }
      return space+"<"+tag+attr+">\n"+data.join("\n")+"\n"+space+"</"+tag+">";
    }
  });

  MML.msubsup.Augment({
    toMathML: function (space) {
      var tag = this.type;
      if (this.data[this.sup] == null) {tag = "msub"}
      if (this.data[this.sub] == null) {tag = "msup"}
      var attr = this.toMathMLattributes();
      delete this.data[0].inferred;
      var data = [];
      for (var i = 0, m = this.data.length; i < m; i++)
        {if (this.data[i]) {data.push(this.data[i].toMathML(space+"  "))}}
      return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">";
    }
  });

  MML.munderover.Augment({
    toMathML: function (space) {
      var tag = this.type;
      if (this.data[this.under] == null) {tag = "mover"}
      if (this.data[this.over] == null)  {tag = "munder"}
      var attr = this.toMathMLattributes();
      delete this.data[0].inferred;
      var data = [];
      for (var i = 0, m = this.data.length; i < m; i++)
        {if (this.data[i]) {data.push(this.data[i].toMathML(space+"  "))}}
      return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">";
    }
  });

  MML.TeXAtom.Augment({
    toMathML: function (space) {
      // FIXME:  Handle spacing using mpadded?
      var attr = this.toMathMLattributes();
      if (!attr && this.data[0].data.length === 1) {return space.substr(2) + this.data[0].toMathML(space)}
      return space+"<mrow"+attr+">\n" + this.data[0].toMathML(space+"  ")+"\n"+space+"</mrow>";
    }
  });

  MML.chars.Augment({
    toMathML: function (space) {return (space||"") + this.toMathMLquote(this.toString())}
  });

  MML.entity.Augment({
    toMathML: function (space) {return (space||"") + "&"+this.data[0]+";<!-- "+this.toString()+" -->"}
  });

  MML.xml.Augment({
   toMathML: function (space) {return (space||"") + this.toString()}
  });

  MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () {
    MML.TeXmathchoice.Augment({
      toMathML: function (space) {return this.Core().toMathML(space)}
    });
  });

  MathJax.Hub.Startup.signal.Post("toMathML Ready");

});

MathJax.Ajax.loadComplete("[MathJax]/extensions/toMathML.js");
<|endoftext|>


<commit_before>var controllerDir;
var appName;

function getAppName() {
    var parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

// Get js-controller directory to load libs
function getControllerDir(isInstall) {
    var fs = require('fs');
    // Find the js-controller location
    var controllerDir = __dirname.replace(/\\/g, '/');
    controllerDir = controllerDir.split('/');
    if (controllerDir[controllerDir.length - 3] === 'adapter') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
    } else if (controllerDir[controllerDir.length - 3] === 'node_modules') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
        if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
            controllerDir += '/node_modules/' + appName + '.js-controller';
        } else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
            controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
        } else if (!fs.existsSync(controllerDir + '/controller.js')) {
            if (!isInstall) {
                console.log('Cannot find js-controller');
                process.exit(10);
            } else {
                process.exit();
            }
        }
    } else if (fs.existsSync(__dirname + '/../../node_modules/' + appName.toLowerCase() + '.js-controller')) {
        controllerDir.splice(controllerDir.length - 2, 2);
        return controllerDir.join('/') + '/node_modules/' + appName.toLowerCase() + '.js-controller';
    } else {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    return controllerDir;
}

// Read controller configuration file
function getConfig() {
    var fs = require('fs');
    if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
    } else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       require(controllerDir + '/lib/adapter.js');
exports.appName =       appName;
<commit_msg>update utils.js<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

let controllerDir;
let appName;

/**
 * returns application name
 *
 * The name of the application can be different and this function finds it out.
 *
 * @returns {string}
 */
 function getAppName() {
    const parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

/**
 * looks for js-controller home folder
 *
 * @param {boolean} isInstall
 * @returns {string}
 */
function getControllerDir(isInstall) {
    // Find the js-controller location
    const possibilities = [
        'iobroker.js-controller',
        'ioBroker.js-controller',
    ];
    /** @type {string} */
    let controllerPath;
    for (const pkg of possibilities) {
        try {
            const possiblePath = require.resolve(pkg);
            if (fs.existsSync(possiblePath)) {
                controllerPath = possiblePath;
                break;
            }
        } catch (e) { /* not found */ }
    }
    if (controllerPath == null) {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    // we found the controller
    return path.dirname(controllerPath);
}

/**
 * reads controller base settings
 *
 * @alias getConfig
 * @returns {object}
 */
 function getConfig() {
    let configPath;
    if (fs.existsSync(
        configPath = path.join(controllerDir, 'conf', appName + '.json')
    )) {
        return JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } else if (fs.existsSync(
        configPath = path.join(controllerDir, 'conf', + appName.toLowerCase() + '.json')
    )) {
        return JSON.parse(fs.readFileSync(configPath, 'utf8'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);
const adapter = require(path.join(controllerDir, 'lib/adapter.js'));

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       adapter;
exports.appName =       appName;
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var router = express.Router();
var extend = require('node.extend');
var request = require('request');
var lxc = require('../lxc');

var timeoutEvents = {};
var label2container = {};
var availContainers = [];
var usedContainers = [];
var workers = {
	clworker0: {
		ip: '104.236.77.157',
		name: 'clworker0'
	}
};

var getFreeMem = function(ip, callback){

	return lxc.exec(
		"python3 -c \"a=`head /proc/meminfo|grep MemAvail|grep -Po '\d+'`;t=`head /proc/meminfo|grep MemTotal|grep -Po '\d+'`;print(round(((t-a)/t)*100, 2))\"",
		ip,
		callback
	);
};

var lxcTimeout = function(container, time){
	time = time || 900000; // 15 minutes
	var keys = Object.keys(timeoutEvents)

	if(keys.indexOf(container.label) !== -1){
		clearTimeout(timeoutEvents[container.label])
	}

	return timeoutEvents[container.label] = setTimeout(function(){
		lxc.stop(name, container);
		return startAll(container.worker);
	}, time);
}

var runner = function(req, res, container){
	lxcTimeout(container);

	var httpOptions = {
		url: 'http://' + container.worker.ip,
		headers: {
			host: container.name
		},
		body: JSON.stringify({
			code: req.body.code
		})
	};

	return request.post(httpOptions, function(error, response, body){
		body = JSON.parse(body);
		body['ip'] = container.label;
		return res.json(body);
	});
};

var startWorkers = function(clworker, stopPercent){
	stopPercent = stopPercent || 81;
	console.log(clworker)
	getFreeMem(clworker.ip, function(usedMemPercent, clworker){
		if(usedMemPercent < 81 ){
			var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
			return lxc.startEphemeral(name, 'crunner0', clworker.ip, function(data){
				console.log('worker:', clworker.name, 'name:', name)
				availContainers.push({
					ip: data.ip,
					name: name,
					worker: clworker,
					label: clworker.name+':'+name
				});
				return startWorkers(clworker);
			});
		}else{
			console.log('using', usedMemPercent, 'percent memory, stopping container creation!', availContainers.length, 'created');
		}
	});
};

startWorkers(workers.clworker0);

router.get('/start/:name', function(req, res, next){
	return lxc.start(req.params.name, function(data){
		if(!data){
			return res.json({
				status: 500,
				name: req.params.name,
				message: data
			});
		}else{
			res.json({});
		}
	});
});

router.get('/live/:template/:name', function(req, res, next){
	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
		console.log('live', arguments);
		return res.json(data);
	});
});

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/clone/:template/:name', function(req, res, next){
	return lxc.clone(req.params.name, req.params.template, function(data){
		console.log('clone', arguments);
		if( data.match(/Created container/) ){
			return res.json({status: 200});
		}else{
			return res.json({status: 500, message: data});
		}
	});
});

router.get('/destroy/:name', function(req, res, next){
	return lxc.destroy(req.params.name, function(data){
		console.log('destroy', arguments);
		if(data){
			return res.json({status: 500, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/info/:name', function(req, res, next){
	return lxc.info(req.params.name, function(data){
		return res.json(data);
	});
});

router.get('/list', function(req, res, next) {
	return lxc.list(function(data){
		return res.json(data);
	});
});

router.post('/run/:ip?', function doRun(req, res, next){
	// check if server is

	return lxc.list(function(data){
		if(!req.params.ip) data = [];
		var container = label2container[req.params.ip] || null;

		if(container){
			return runner(req, res, container);
		}else{
			container = availContainers.splice(0,1);
			label2container[container.worker.name+':'+container.name] = container;
			return runner(req, res, container);
		}
	});

});

module.exports = router;
<commit_msg>closer<commit_after>'use strict';

var express = require('express');
var router = express.Router();
var extend = require('node.extend');
var request = require('request');
var lxc = require('../lxc');

var timeoutEvents = {};
var label2container = {};
var availContainers = [];
var usedContainers = [];
var workers = {
	clworker0: {
		ip: '104.236.77.157',
		name: 'clworker0'
	}
};

var getFreeMem = function(ip, callback){

	return lxc.exec(
		"python3 -c \"a=`head /proc/meminfo|grep MemAvail|grep -Po '\\d+'`;t=`head /proc/meminfo|grep MemTotal|grep -Po '\\d+'`;print(round(((t-a)/t)*100, 2))\"",
		ip,
		callback
	);
};

var lxcTimeout = function(container, time){
	time = time || 900000; // 15 minutes
	var keys = Object.keys(timeoutEvents)

	if(keys.indexOf(container.label) !== -1){
		clearTimeout(timeoutEvents[container.label])
	}

	return timeoutEvents[container.label] = setTimeout(function(){
		lxc.stop(name, container);
		return startAll(container.worker);
	}, time);
}

var runner = function(req, res, container){
	lxcTimeout(container);

	var httpOptions = {
		url: 'http://' + container.worker.ip,
		headers: {
			host: container.name
		},
		body: JSON.stringify({
			code: req.body.code
		})
	};

	return request.post(httpOptions, function(error, response, body){
		body = JSON.parse(body);
		body['ip'] = container.label;
		return res.json(body);
	});
};

var startWorkers = function(clworker, stopPercent){
	stopPercent = stopPercent || 81;
	console.log(clworker)
	getFreeMem(clworker.ip, function(usedMemPercent, clworker){
		if(usedMemPercent < 81 ){
			var name = 'crunner-'+(Math.random()*100).toString().replace('.','');
			return lxc.startEphemeral(name, 'crunner0', clworker.ip, function(data){
				console.log('worker:', clworker.name, 'name:', name)
				availContainers.push({
					ip: data.ip,
					name: name,
					worker: clworker,
					label: clworker.name+':'+name
				});
				return startWorkers(clworker);
			});
		}else{
			console.log('using', usedMemPercent, 'percent memory, stopping container creation!', availContainers.length, 'created');
		}
	});
};

startWorkers(workers.clworker0);

router.get('/start/:name', function(req, res, next){
	return lxc.start(req.params.name, function(data){
		if(!data){
			return res.json({
				status: 500,
				name: req.params.name,
				message: data
			});
		}else{
			res.json({});
		}
	});
});

router.get('/live/:template/:name', function(req, res, next){
	return lxc.startEphemeral(req.params.name, req.params.template, function (data) {
		console.log('live', arguments);
		return res.json(data);
	});
});

router.get('/stop/:name', function(req, res, next){
	return lxc.stop(req.params.name, function(data){
		console.log('stop', arguments);
		if(data){
			return res.json({status: 500, name: req.params.name, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/clone/:template/:name', function(req, res, next){
	return lxc.clone(req.params.name, req.params.template, function(data){
		console.log('clone', arguments);
		if( data.match(/Created container/) ){
			return res.json({status: 200});
		}else{
			return res.json({status: 500, message: data});
		}
	});
});

router.get('/destroy/:name', function(req, res, next){
	return lxc.destroy(req.params.name, function(data){
		console.log('destroy', arguments);
		if(data){
			return res.json({status: 500, message: data});
		}else{
			return res.json({status: 200});
		}
	});
});

router.get('/info/:name', function(req, res, next){
	return lxc.info(req.params.name, function(data){
		return res.json(data);
	});
});

router.get('/list', function(req, res, next) {
	return lxc.list(function(data){
		return res.json(data);
	});
});

router.post('/run/:ip?', function doRun(req, res, next){
	// check if server is

	return lxc.list(function(data){
		if(!req.params.ip) data = [];
		var container = label2container[req.params.ip] || null;

		if(container){
			return runner(req, res, container);
		}else{
			container = availContainers.splice(0,1);
			label2container[container.worker.name+':'+container.name] = container;
			return runner(req, res, container);
		}
	});

});

module.exports = router;
<|endoftext|>