<commit_before>//////////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License (MIT)
//
//  Copyright (c) 2017-present, Dom Chen
//  All rights reserved.
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy of
//  this software and associated documentation files (the "Software"), to deal in the
//  Software without restriction, including without limitation the rights to use, copy,
//  modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
//  and to permit persons to whom the Software is furnished to do so, subject to the
//  following conditions:
//
//      The above copyright notice and this permission notice shall be included in all
//      copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////////////

const Utils = require('./Utils')
const fs = require("fs");
const path = require("path");
const crypto = require('crypto')

function findConfigFile(searchPath) {
    while (true) {
        let fileName = path.join(searchPath, "DEPS");
        if (fs.existsSync(fileName)) {
            return fileName;
        }
        let parentPath = path.dirname(searchPath);
        if (parentPath === searchPath) {
            break;
        }
        searchPath = parentPath;
    }
    return "";
}

function getHash(content) {
    let hash = crypto.createHash('sha1')
    hash.update(content)
    return hash.digest('hex')
}

function parseFiles(files, vars, projectPath) {
    if (!files) {
        return [];
    }
    let list = [];
    for (let item of files) {
        item.url = formatString(item.url, vars);
        item.dir = formatString(item.dir, vars);
        item.hash = getHash(item.url);
        item.dir = path.resolve(projectPath, item.dir);
        item.hashFile = "." + path.basename(item.url) + ".sha1";
        item.hashFile = path.resolve(item.dir, item.hashFile);
        let unzip = item.unzip;
        if (typeof unzip == "string") {
            unzip = formatString(unzip, vars);
            item.unzip = (unzip === "true");
        } else if (typeof unzip != "boolean") {
            item.unzip = false;
        }
        list.push(item);
    }
    return list;
}

function parseRepos(repos, vars, projectPath) {
    if (!repos) {
        return [];
    }
    let list = [];
    for (let item of repos) {
        item.url = formatString(item.url, vars);
        item.commit = formatString(item.commit, vars);
        item.dir = formatString(item.dir, vars);
        item.dir = path.resolve(projectPath, item.dir);
        list.push(item);
    }
    return list;
}

function parseActions(actions, vars, projectPath) {
    if (!actions) {
        return [];
    }
    let list = [];
    for (let item of actions) {
        list.push(item);
        item.command = formatString(item.command, vars);
        item.dir = formatString(item.dir, vars);
        item.dir = path.resolve(projectPath, item.dir);
    }
    return list;
}

function formatString(text, vars) {
    let index = text.indexOf("${");
    while (index !== -1) {
        let prefix = text.substring(0, index);
        text = text.substring(index);
        index = text.indexOf("}");
        if (index === -1) {
            text = prefix + text;
            break;
        }
        let key = text.substring(2, index);
        text = text.substring(index + 1);
        let value = vars[key] ? vars[key] : "${" + key + "}";
        text = prefix + value + text;
        index = text.indexOf("${");
    }
    return text;
}

function filterByPlatform(items, hostPlatform) {
    if (!items) {
        return [];
    }
    let list = [];
    let platforms = Object.keys(items);
    for (let platform of platforms) {
        if (!hostPlatform || platform === hostPlatform || platform === "common") {
            for (let item of items[platform]) {
                list.push(item);
            }
        }
    }
    return list;
}

function compareVersion(versionA, versionB) {
    if (versionA === versionB) {
        return 0;
    }
    let listA = versionA.split(".");
    let listB = versionB.split(".");
    let length = Math.max(listA.length, listB.length);
    for (let i = 0; i < length; i++) {
        if (listA.length <= i) {
            return -1;
        }
        let a = parseInt(listA[i]);
        if (listB.length <= i) {
            return 1;
        }
        let b = parseInt(listB[i]);
        if (a === b) {
            continue;
        }
        return a > b ? 1 : -1;
    }
    return 0;
}

function parse(configFileName, version, platform) {
    if (!fs.existsSync(configFileName)) {
        return null;
    }
    let jsonText = Utils.readFile(configFileName);
    let data;
    try {
        data = JSON.parse(jsonText);
    } catch (e) {
        if (jsonText.trimLeft().indexOf("{") === 0) {
            Utils.error("The DEPS config file is not a valid JSON file: " + configFileName);
        }
        return null;
    }
    let projectPath = path.dirname(configFileName);
    let config = {};
    config.version = data.version ? data.version : "0.0.0";
    if (compareVersion(version, config.version) < 0) {
        Utils.error("The DEPS config requires a high depsync tool version: " + configFileName);
        Utils.error("Requires version: " + config.version);
        Utils.error("Current version: " + version);
        Utils.error("Please update the depsync tool and try again.");
        return null;
    }
    let files = filterByPlatform(data.files, platform);
    config.files = parseFiles(files, data.vars, projectPath);
    let repos = filterByPlatform(data.repos, platform);
    config.repos = parseRepos(repos, data.vars, projectPath);
    let actions = filterByPlatform(data.actions, platform);
    config.actions = parseActions(actions, data.vars, projectPath);
    return config;
}

exports.findConfigFile = findConfigFile;
exports.parse = parse;<commit_msg>Update the description of version comparing result.<commit_after>//////////////////////////////////////////////////////////////////////////////////////
//
//  The MIT License (MIT)
//
//  Copyright (c) 2017-present, Dom Chen
//  All rights reserved.
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy of
//  this software and associated documentation files (the "Software"), to deal in the
//  Software without restriction, including without limitation the rights to use, copy,
//  modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
//  and to permit persons to whom the Software is furnished to do so, subject to the
//  following conditions:
//
//      The above copyright notice and this permission notice shall be included in all
//      copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
//  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
//  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
//  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
//  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////////////

const Utils = require('./Utils')
const fs = require("fs");
const path = require("path");
const crypto = require('crypto')

function findConfigFile(searchPath) {
    while (true) {
        let fileName = path.join(searchPath, "DEPS");
        if (fs.existsSync(fileName)) {
            return fileName;
        }
        let parentPath = path.dirname(searchPath);
        if (parentPath === searchPath) {
            break;
        }
        searchPath = parentPath;
    }
    return "";
}

function getHash(content) {
    let hash = crypto.createHash('sha1')
    hash.update(content)
    return hash.digest('hex')
}

function parseFiles(files, vars, projectPath) {
    if (!files) {
        return [];
    }
    let list = [];
    for (let item of files) {
        item.url = formatString(item.url, vars);
        item.dir = formatString(item.dir, vars);
        item.hash = getHash(item.url);
        item.dir = path.resolve(projectPath, item.dir);
        item.hashFile = "." + path.basename(item.url) + ".sha1";
        item.hashFile = path.resolve(item.dir, item.hashFile);
        let unzip = item.unzip;
        if (typeof unzip == "string") {
            unzip = formatString(unzip, vars);
            item.unzip = (unzip === "true");
        } else if (typeof unzip != "boolean") {
            item.unzip = false;
        }
        list.push(item);
    }
    return list;
}

function parseRepos(repos, vars, projectPath) {
    if (!repos) {
        return [];
    }
    let list = [];
    for (let item of repos) {
        item.url = formatString(item.url, vars);
        item.commit = formatString(item.commit, vars);
        item.dir = formatString(item.dir, vars);
        item.dir = path.resolve(projectPath, item.dir);
        list.push(item);
    }
    return list;
}

function parseActions(actions, vars, projectPath) {
    if (!actions) {
        return [];
    }
    let list = [];
    for (let item of actions) {
        list.push(item);
        item.command = formatString(item.command, vars);
        item.dir = formatString(item.dir, vars);
        item.dir = path.resolve(projectPath, item.dir);
    }
    return list;
}

function formatString(text, vars) {
    let index = text.indexOf("${");
    while (index !== -1) {
        let prefix = text.substring(0, index);
        text = text.substring(index);
        index = text.indexOf("}");
        if (index === -1) {
            text = prefix + text;
            break;
        }
        let key = text.substring(2, index);
        text = text.substring(index + 1);
        let value = vars[key] ? vars[key] : "${" + key + "}";
        text = prefix + value + text;
        index = text.indexOf("${");
    }
    return text;
}

function filterByPlatform(items, hostPlatform) {
    if (!items) {
        return [];
    }
    let list = [];
    let platforms = Object.keys(items);
    for (let platform of platforms) {
        if (!hostPlatform || platform === hostPlatform || platform === "common") {
            for (let item of items[platform]) {
                list.push(item);
            }
        }
    }
    return list;
}

function compareVersion(versionA, versionB) {
    if (versionA === versionB) {
        return 0;
    }
    let listA = versionA.split(".");
    let listB = versionB.split(".");
    let length = Math.max(listA.length, listB.length);
    for (let i = 0; i < length; i++) {
        if (listA.length <= i) {
            return -1;
        }
        let a = parseInt(listA[i]);
        if (listB.length <= i) {
            return 1;
        }
        let b = parseInt(listB[i]);
        if (a === b) {
            continue;
        }
        return a > b ? 1 : -1;
    }
    return 0;
}

function parse(configFileName, version, platform) {
    if (!fs.existsSync(configFileName)) {
        return null;
    }
    let jsonText = Utils.readFile(configFileName);
    let data;
    try {
        data = JSON.parse(jsonText);
    } catch (e) {
        if (jsonText.trimLeft().indexOf("{") === 0) {
            Utils.error("The DEPS config file is not a valid JSON file: " + configFileName);
        }
        return null;
    }
    let projectPath = path.dirname(configFileName);
    let config = {};
    config.version = data.version ? data.version : "0.0.0";
    if (compareVersion(version, config.version) < 0) {
        Utils.error("The DEPS config requires a higher version of depsync tool: " + configFileName);
        Utils.error("Requires version: " + config.version);
        Utils.error("Current version: " + version);
        Utils.error("Please update the depsync tool and try again.");
        return null;
    }
    let files = filterByPlatform(data.files, platform);
    config.files = parseFiles(files, data.vars, projectPath);
    let repos = filterByPlatform(data.repos, platform);
    config.repos = parseRepos(repos, data.vars, projectPath);
    let actions = filterByPlatform(data.actions, platform);
    config.actions = parseActions(actions, data.vars, projectPath);
    return config;
}

exports.findConfigFile = findConfigFile;
exports.parse = parse;<|endoftext|>


<commit_before>import fs from 'fs'
import path from 'path'
import JSON5 from 'json5'
import {merge} from './Utils/merge'

export class Config {
	_repository = null

	constructor(app = null) {
		this._repository = { }

		if(!app.isNil) {
			this.populate(app)
		}
	}

	get(keyPath, fallback = null) {
		const keys = keyPath.split('.')
		let value = this._repository[keys.shift()]

		if(value.isNil) {
			return fallback
		}

		for(const key of keys) {
			if(value.isNil) {
				continue
			}

			value = value[key]
		}

		return value || fallback
	}

	has(keyPath) {
		const value = this.get(keyPath)
		return !value.isNil
	}

	set(keyPath, value) {
		const keys = keyPath.split('.')
		const last = keys.pop()
		let object = this._repository

		if(keys.length > 0) {
			for(const key of keys) {
				if(object.isNil) {
					continue
				}

				object = object[key]
			}
		}

		if(object) {
			object[last] = value
		}
	}

	populate(app) {
		let dir = app.paths.config()

		const exists = path => {
			try {
				// eslint-disable-next-line no-sync
				fs.accessSync(path, fs.F_OK)
				return true
			} catch(e) {
				return false
			}
		}

		if(!exists(dir)) {
			console.error('Unable to populate config, path does not exist', dir)
			return
		}

		const files = { }

		this._populateConfigFiles(files, dir)

		for(const env of app.env().split('.')) {
			dir = path.join(dir, env)

			if(exists(dir)) {
				this._populateConfigFiles(files, dir)
			} else {
				break
			}
		}

		for(const group in files) {
			if(group === '.env') {
				continue
			}

			for(const file of files[group]) {
				// eslint-disable-next-line no-sync
				const config = JSON5.parse(fs.readFileSync(file))
				this._repository[group] = merge(this._repository[group] || { }, config)
			}
		}

		if(files['.env']) {
			for(const file of files['.env']) {
				// eslint-disable-next-line no-sync
				const config = JSON5.parse(fs.readFileSync(file))

				for(const group in config) {
					this._repository[group] = merge(this._repository[group] || { }, config[group])
				}
			}
		}
	}

	_populateConfigFiles(files, dir) {
		for(const file of fs.readdirSync(dir)) { // eslint-disable-line no-sync
			if(path.extname(file) !== '.json') {
				continue
			}

			const name = path.basename(file, '.json')

			if(!files[name]) {
				files[name] = [ ]
			}

			files[name].push(path.join(dir, path.basename(file)))
		}
	}
}
<commit_msg>Added `Config.loadDefault(group, file)` for providers to inject in default config<commit_after>import fs from 'fs'
import path from 'path'
import JSON5 from 'json5'
import {merge} from './Utils/merge'

export class Config {
	_repository = null

	constructor(app = null) {
		this._repository = { }

		if(!app.isNil) {
			this.populate(app)
		}
	}

	get(keyPath, fallback = null) {
		const keys = keyPath.split('.')
		let value = this._repository[keys.shift()]

		if(value.isNil) {
			return fallback
		}

		for(const key of keys) {
			if(value.isNil) {
				continue
			}

			value = value[key]
		}

		return value || fallback
	}

	has(keyPath) {
		const value = this.get(keyPath)
		return !value.isNil
	}

	set(keyPath, value) {
		const keys = keyPath.split('.')
		const last = keys.pop()
		let object = this._repository

		if(keys.length > 0) {
			for(const key of keys) {
				if(object.isNil) {
					continue
				}

				object = object[key]
			}
		}

		if(object) {
			object[last] = value
		}
	}

	loadDefault(group, file) {
		// eslint-disable-next-line no-sync
		const config = JSON5.parse(fs.readFileSync(file))
		const existing = this._repository[group] || { }
		this._repository[group] = merge(merge({ }, config), existing)
	}

	populate(app) {
		let dir = app.paths.config()

		const exists = path => {
			try {
				// eslint-disable-next-line no-sync
				fs.accessSync(path, fs.F_OK)
				return true
			} catch(e) {
				return false
			}
		}

		if(!exists(dir)) {
			console.error('Unable to populate config, path does not exist', dir)
			return
		}

		const files = { }

		this._populateConfigFiles(files, dir)

		for(const env of app.env().split('.')) {
			dir = path.join(dir, env)

			if(exists(dir)) {
				this._populateConfigFiles(files, dir)
			} else {
				break
			}
		}

		for(const group in files) {
			if(group === '.env') {
				continue
			}

			for(const file of files[group]) {
				// eslint-disable-next-line no-sync
				const config = JSON5.parse(fs.readFileSync(file))
				this._repository[group] = merge(this._repository[group] || { }, config)
			}
		}

		if(files['.env']) {
			for(const file of files['.env']) {
				// eslint-disable-next-line no-sync
				const config = JSON5.parse(fs.readFileSync(file))

				for(const group in config) {
					this._repository[group] = merge(this._repository[group] || { }, config[group])
				}
			}
		}
	}

	_populateConfigFiles(files, dir) {
		for(const file of fs.readdirSync(dir)) { // eslint-disable-line no-sync
			if(path.extname(file) !== '.json') {
				continue
			}

			const name = path.basename(file, '.json')

			if(!files[name]) {
				files[name] = [ ]
			}

			files[name].push(path.join(dir, path.basename(file)))
		}
	}
}
<|endoftext|>


<commit_before>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * 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.
 */

define('Sage/Platform/Mobile/Detail', ['Sage/Platform/Mobile/View', 'Sage/Platform/Mobile/Utility', 'Sage/Platform/Mobile/Format'], function() {

    return dojo.declare('Sage.Platform.Mobile.Detail', [Sage.Platform.Mobile.View], {
        attributeMap: {
            detailContent: {node: 'contentNode', type: 'innerHTML'}
        },
        widgetTemplate: new Simplate([
            '
',
            '{%! $.loadingTemplate %}',
            '

',
            '
'
        ]),
        emptyTemplate: new Simplate([
        ]),
        loadingTemplate: new Simplate([
            '
',
            '

{%: $.loadingText %}

',
            '
'
        ]),
        sectionBeginTemplate: new Simplate([
            '
',
            '{%: ($.title || $.options.title) %}<button class="collapsed-indicator" aria-label="{%: $$.toggleCollapseText %}"></button>',
            '
',
            '{% if ($.list || $.options.list) { %}',
            '
',
            '{% } else { %}',
            '
',
            '{% } %}'
        ]),
        sectionEndTemplate: new Simplate([
            '{% if ($.list || $.options.list) { %}',
            '
',
            '{% } else { %}',
            '
',
            '{% } %}'
        ]),
        propertyTemplate: new Simplate([
            '
',
            '{%: $.label %}',
            '{%= $.value %}', // todo: create a way to allow the value to not be surrounded with a span tag
            '
'
        ]),
        relatedPropertyTemplate: new Simplate([
            '
',
            '{%: $.label %}',
            '',
            '',
            '{%= $.value %}',
            '',
            '',
            '
'
        ]),
        relatedTemplate: new Simplate([
            '
',
            '',
            '{% if ($.icon) { %}',
            '',
            '{% } %}',
            '{%: $.label %}',
            '',
            '
'
        ]),
        actionPropertyTemplate: new Simplate([
            '
',
            '{%: $.label %}',
            '',
            '',
            '{%= $.value %}',
            '',
            '',
            '
'
        ]),
        actionTemplate: new Simplate([
            '
',
            '',
            '{% if ($.icon) { %}',
            '',
            '{% } %}',
            '{%: $.label %}',
            '{%= $.value %}',
            '',
            '
'
        ]),
        notAvailableTemplate: new Simplate([
            '
{%: $.notAvailableText %}
'
        ]),
        id: 'generic_detail',
        layout: null,
        security: false,
        customizationSet: 'detail',
        expose: false,
        editText: 'Edit',
        titleText: 'Detail',
        detailsText: 'Details',
        toggleCollapseText: 'toggle collapse',
        loadingText: 'loading...',
        requestErrorText: 'A server error occurred while requesting data.',
        notAvailableText: 'The requested entry is not available.',
        editView: false,

        postCreate: function() {
            this.inherited(arguments);
            this.connect(App, 'onRefresh', this._onRefresh);
            this.clear();
        },
        createToolLayout: function() {
            return this.tools || (this.tools = {
                'tbar': [{
                    id: 'edit',
                    action: 'navigateToEditView',
                    security: App.getViewSecurity(this.editView, 'update')
                }]
            });
        },
        invokeAction: function(name, parameters, evt, el) {
            if (parameters && /true/i.test(parameters['disableAction']))
                return;

            return this.inherited(arguments);
        },
        _onRefresh: function(o) {
            var descriptor = o.data && o.data['$descriptor'];

            if (this.options && this.options.key === o.key) {
                this.refreshRequired = true;

                if (descriptor) {
                    this.options.title = descriptor;
                    this.set('title', descriptor);
                }
            }
        },
        formatRelatedQuery: function(entry, fmt, property) {
            var property = property || '$key';

            return dojo.string.substitute(fmt, [Sage.Platform.Mobile.Utility.getValue(entry, property)]);
        },
        toggleSection: function(params) {
            var node = dojo.byId(params.$source);
            if (node)
                dojo.toggleClass(node, 'collapsed');
        },
        activateRelatedEntry: function(params) {
            if (params.context)
                this.navigateToRelatedView(params.view, dojo.fromJson(params.context), params.descriptor);
        },
        activateRelatedList: function(params) {
            if (params.context)
                this.navigateToRelatedView(params.view, dojo.fromJson(params.context), params.descriptor);
        },
        navigateToEditView: function(el) {
            var view = App.getView(this.editView);
            if (view)
                view.show({entry: this.entry});
        },
        navigateToRelatedView: function(view, o, descriptor) {
            var context,
                targetView;
            if (typeof o === 'string') {
                context = {
                    key: o,
                    descriptor: descriptor
                };
            } else {
                context = o;

                if (descriptor) context['descriptor'] = descriptor;
            }

            if (context) {
                targetView = App.getView(view);
                if (targetView)
                    targetView.show(context);
            }
        },
        createRequest: function() {
            var request = new Sage.SData.Client.SDataSingleResourceRequest(this.getService());

            /* test for complex selector */
            /* todo: more robust test required? */
            if (/(\s+)/.test(this.options.key))
                request.setResourceSelector(this.options.key);
            else
                request.setResourceSelector(dojo.string.substitute("'${0}'", [this.options.key]));

            if (this.resourceKind)
                request.setResourceKind(this.resourceKind);

            if (this.querySelect)
                request.setQueryArg(Sage.SData.Client.SDataUri.QueryArgNames.Select, this.querySelect.join(','));

            if (this.queryInclude)
                request.setQueryArg(Sage.SData.Client.SDataUri.QueryArgNames.Include, this.queryInclude.join(','));

            if (this.queryOrderBy)
                request.setQueryArg(Sage.SData.Client.SDataUri.QueryArgNames.OrderBy, this.queryOrderBy);

            return request;
        },
        createLayout: function() {
            return this.layout || [];
        },
        processLayout: function(layout, entry)
        {
            var rows = (layout['children'] || layout['as'] || layout),
                options = layout['options'] || (layout['options'] = {
                    title: this.detailsText
                }),
                getValue = Sage.Platform.Mobile.Utility.getValue,
                encodeValue = Sage.Platform.Mobile.Format.encode,
                sectionQueue = [],
                sectionStarted = false,
                content = [];

            for (var i = 0; i < rows.length; i++) {
                var current = rows[i],
                    include = this.expandExpression(current['include'], entry),
                    exclude = this.expandExpression(current['exclude'], entry);

                if (include !== undefined && !include) continue;
                if (exclude !== undefined && exclude) continue;

                if (current['children'] || current['as'])
                {
                    if (sectionStarted)
                        sectionQueue.push(current);
                    else
                        this.processLayout(current, entry);

                    continue;
                }

                if (!sectionStarted)
                {
                    sectionStarted = true;
                    content.push(this.sectionBeginTemplate.apply(layout, this));
                }

                var provider = current['provider'] || getValue,
                    property = typeof current['property'] == 'string'
                        ? current['property']
                        : current['name'],
                    value = typeof current['value'] === 'undefined'
                        ? provider(entry, property)
                        : current['value'],
                    rendered,
                    formatted;

                if (current['template'] || current['tpl'])
                {
                    rendered = (current['template'] || current['tpl']).apply(value, this);
                    formatted = current['encode'] === true
                        ? encodeValue(rendered)
                        : rendered;
                }
                else if (current['renderer'] && typeof current['renderer'] === 'function')
                {
                    rendered = current['renderer'].call(this, value);
                    formatted = current['encode'] === true
                        ? encodeValue(rendered)
                        : rendered;
                }
                else
                {
                    formatted = current['encode'] !== false
                        ? encodeValue(value)
                        : value;
                }

                var data = dojo.mixin({}, {
                    entry: entry,
                    value: formatted,
                    raw: value
                }, current);

                if (current['descriptor'])
                    data['descriptor'] = typeof current['descriptor'] === 'function'
                        ? this.expandExpression(current['descriptor'], entry, value)
                        : provider(entry, current['descriptor']);

                if (current['action'])
                    data['action'] = this.expandExpression(current['action'], entry, value);

                var hasAccess = App.hasAccessTo(current['security']);
                if (current['security'])
                    data['disabled'] = !hasAccess;

                if (current['disabled'] && hasAccess)
                    data['disabled'] = this.expandExpression(current['disabled'], entry, value);

                if (current['view']) {
                    var context = {};
                    if (current['key'])
                        context['key'] = typeof current['key'] === 'function'
                            ? this.expandExpression(current['key'], entry)
                            : provider(entry, current['key']);
                    if (current['where'])
                        context['where'] = this.expandExpression(current['where'], entry);
                    if (current['resourceKind'])
                        context['resourceKind'] = this.expandExpression(current['resourceKind'], entry);
                    if (current['resourceProperty'])
                        context['resourceProperty'] = this.expandExpression(current['resourceProperty'], entry);
                    if (current['resourcePredicate'])
                        context['resourcePredicate'] = this.expandExpression(current['resourcePredicate'], entry);

                    data['view'] = current['view'];
                    data['context'] = dojo.toJson(context);
                }

                // priority: wrap > (relatedPropertyTemplate | relatedTemplate) > (actionPropertyTemplate | actionTemplate) > propertyTemplate
                var useListTemplate = (layout['list'] || options['list']),
                    template = current['use']
                        ? current['use']
                        : current['view']
                            ? useListTemplate
                                ? this.relatedTemplate
                                : this.relatedPropertyTemplate
                            : current['action']
                                ? useListTemplate
                                    ? this.actionTemplate
                                    : this.actionPropertyTemplate
                                : this.propertyTemplate;

                content.push(template.apply(data, this));
            }

            if (sectionStarted) content.push(this.sectionEndTemplate.apply(layout, this));

            dojo.query(this.contentNode).append(content.join(''));

            for (var i = 0; i < sectionQueue.length; i++)
            {
                var current = sectionQueue[i];

                this.processLayout(current, entry);
            }
        },
        processEntry: function(entry) {
            this.entry = entry;

            if (this.entry)
            {
                this.processLayout(this._createCustomizedLayout(this.createLayout()), this.entry);
            }
            else
            {
                this.set('detailContent', '');
            }
        },
        onRequestDataFailure: function(response, o) {
            if (response && response.status == 404)
                dojo.query(this.contentNode).append(this.notAvailableTemplate.apply(this));
            else
                alert(dojo.string.substitute(this.requestErrorText, [response, o]));

            dojo.removeClass(this.domNode, 'panel-loading');
        },
        onRequestDataAborted: function(response, o) {
            this.options = false; // force a refresh
            dojo.removeClass(this.domNode, 'panel-loading');
        },
        onRequestDataSuccess: function(entry) {
            this.processEntry(entry);
            dojo.removeClass(this.domNode, 'panel-loading');
        },
        requestData: function() {
            dojo.addClass(this.domNode, 'panel-loading');

            var request = this.createRequest();
            if (request)
                request.read({
                    success: this.onRequestDataSuccess,
                    failure: this.onRequestDataFailure,
                    aborted: this.onRequestDataAborted,
                    scope: this
                });
        },
        refreshRequiredFor: function(options) {
            if (this.options) {
                if (options) {
                    if (this.options.key !== options.key) return true;
                }
                return false;
            }
            else
                return this.inherited(arguments);
        },
        activate: function(tag, data) {
            var options = data && data.options;
            if (options && options.descriptor)
                options.title = options.title || options.descriptor;

            this.inherited(arguments);
        },
        show: function(options) {
            if (options && options.descriptor)
                options.title = options.title || options.descriptor;

            this.inherited(arguments);
        },
        getTag: function() {
            return this.options && this.options.key;
        },
        getContext: function() {
            return dojo.mixin(this.inherited(arguments), {
                resourceKind: this.resourceKind,
                key: this.options.key,
                descriptor: this.options.descriptor
            });
        },
        beforeTransitionTo: function() {
            this.inherited(arguments);

            this.canEdit = this.editor ? true : false;

            if (this.refreshRequired)
            {
                this.clear();
            }
        },
        refresh: function() {
            if (this.security && !App.hasAccessTo(this.expandExpression(this.security)))
            {
                dojo.query(this.contentNode).append(this.notAvailableTemplate.apply(this));
                return;
            }

            this.requestData();
        },
        transitionTo: function() {
            this.inherited(arguments);
        },
        clear: function() {
            this.set('detailContent', this.emptyTemplate.apply(this));
            this.context = false;
        }
    });
});
<commit_msg>added fallback for template and renderer value<commit_after>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * 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.
 */

define('Sage/Platform/Mobile/Detail', ['Sage/Platform/Mobile/View', 'Sage/Platform/Mobile/Utility', 'Sage/Platform/Mobile/Format'], function() {

    return dojo.declare('Sage.Platform.Mobile.Detail', [Sage.Platform.Mobile.View], {
        attributeMap: {
            detailContent: {node: 'contentNode', type: 'innerHTML'}
        },
        widgetTemplate: new Simplate([
            '
',
            '{%! $.loadingTemplate %}',
            '

',
            '
'
        ]),
        emptyTemplate: new Simplate([
        ]),
        loadingTemplate: new Simplate([
            '
',
            '

{%: $.loadingText %}

',
            '
'
        ]),
        sectionBeginTemplate: new Simplate([
            '
',
            '{%: ($.title || $.options.title) %}<button class="collapsed-indicator" aria-label="{%: $$.toggleCollapseText %}"></button>',
            '
',
            '{% if ($.list || $.options.list) { %}',
            '
',
            '{% } else { %}',
            '
',
            '{% } %}'
        ]),
        sectionEndTemplate: new Simplate([
            '{% if ($.list || $.options.list) { %}',
            '
',
            '{% } else { %}',
            '
',
            '{% } %}'
        ]),
        propertyTemplate: new Simplate([
            '
',
            '{%: $.label %}',
            '{%= $.value %}', // todo: create a way to allow the value to not be surrounded with a span tag
            '
'
        ]),
        relatedPropertyTemplate: new Simplate([
            '
',
            '{%: $.label %}',
            '',
            '',
            '{%= $.value %}',
            '',
            '',
            '
'
        ]),
        relatedTemplate: new Simplate([
            '
',
            '',
            '{% if ($.icon) { %}',
            '',
            '{% } %}',
            '{%: $.label %}',
            '',
            '
'
        ]),
        actionPropertyTemplate: new Simplate([
            '
',
            '{%: $.label %}',
            '',
            '',
            '{%= $.value %}',
            '',
            '',
            '
'
        ]),
        actionTemplate: new Simplate([
            '
',
            '',
            '{% if ($.icon) { %}',
            '',
            '{% } %}',
            '{%: $.label %}',
            '{%= $.value %}',
            '',
            '
'
        ]),
        notAvailableTemplate: new Simplate([
            '
{%: $.notAvailableText %}
'
        ]),
        id: 'generic_detail',
        layout: null,
        security: false,
        customizationSet: 'detail',
        expose: false,
        editText: 'Edit',
        titleText: 'Detail',
        detailsText: 'Details',
        toggleCollapseText: 'toggle collapse',
        loadingText: 'loading...',
        requestErrorText: 'A server error occurred while requesting data.',
        notAvailableText: 'The requested entry is not available.',
        editView: false,

        postCreate: function() {
            this.inherited(arguments);
            this.connect(App, 'onRefresh', this._onRefresh);
            this.clear();
        },
        createToolLayout: function() {
            return this.tools || (this.tools = {
                'tbar': [{
                    id: 'edit',
                    action: 'navigateToEditView',
                    security: App.getViewSecurity(this.editView, 'update')
                }]
            });
        },
        invokeAction: function(name, parameters, evt, el) {
            if (parameters && /true/i.test(parameters['disableAction']))
                return;

            return this.inherited(arguments);
        },
        _onRefresh: function(o) {
            var descriptor = o.data && o.data['$descriptor'];

            if (this.options && this.options.key === o.key) {
                this.refreshRequired = true;

                if (descriptor) {
                    this.options.title = descriptor;
                    this.set('title', descriptor);
                }
            }
        },
        formatRelatedQuery: function(entry, fmt, property) {
            var property = property || '$key';

            return dojo.string.substitute(fmt, [Sage.Platform.Mobile.Utility.getValue(entry, property)]);
        },
        toggleSection: function(params) {
            var node = dojo.byId(params.$source);
            if (node)
                dojo.toggleClass(node, 'collapsed');
        },
        activateRelatedEntry: function(params) {
            if (params.context)
                this.navigateToRelatedView(params.view, dojo.fromJson(params.context), params.descriptor);
        },
        activateRelatedList: function(params) {
            if (params.context)
                this.navigateToRelatedView(params.view, dojo.fromJson(params.context), params.descriptor);
        },
        navigateToEditView: function(el) {
            var view = App.getView(this.editView);
            if (view)
                view.show({entry: this.entry});
        },
        navigateToRelatedView: function(view, o, descriptor) {
            var context,
                targetView;
            if (typeof o === 'string') {
                context = {
                    key: o,
                    descriptor: descriptor
                };
            } else {
                context = o;

                if (descriptor) context['descriptor'] = descriptor;
            }

            if (context) {
                targetView = App.getView(view);
                if (targetView)
                    targetView.show(context);
            }
        },
        createRequest: function() {
            var request = new Sage.SData.Client.SDataSingleResourceRequest(this.getService());

            /* test for complex selector */
            /* todo: more robust test required? */
            if (/(\s+)/.test(this.options.key))
                request.setResourceSelector(this.options.key);
            else
                request.setResourceSelector(dojo.string.substitute("'${0}'", [this.options.key]));

            if (this.resourceKind)
                request.setResourceKind(this.resourceKind);

            if (this.querySelect)
                request.setQueryArg(Sage.SData.Client.SDataUri.QueryArgNames.Select, this.querySelect.join(','));

            if (this.queryInclude)
                request.setQueryArg(Sage.SData.Client.SDataUri.QueryArgNames.Include, this.queryInclude.join(','));

            if (this.queryOrderBy)
                request.setQueryArg(Sage.SData.Client.SDataUri.QueryArgNames.OrderBy, this.queryOrderBy);

            return request;
        },
        createLayout: function() {
            return this.layout || [];
        },
        processLayout: function(layout, entry)
        {
            var rows = (layout['children'] || layout['as'] || layout),
                options = layout['options'] || (layout['options'] = {
                    title: this.detailsText
                }),
                getValue = Sage.Platform.Mobile.Utility.getValue,
                encodeValue = Sage.Platform.Mobile.Format.encode,
                sectionQueue = [],
                sectionStarted = false,
                content = [];

            for (var i = 0; i < rows.length; i++) {
                var current = rows[i],
                    include = this.expandExpression(current['include'], entry),
                    exclude = this.expandExpression(current['exclude'], entry);

                if (include !== undefined && !include) continue;
                if (exclude !== undefined && exclude) continue;

                if (current['children'] || current['as'])
                {
                    if (sectionStarted)
                        sectionQueue.push(current);
                    else
                        this.processLayout(current, entry);

                    continue;
                }

                if (!sectionStarted)
                {
                    sectionStarted = true;
                    content.push(this.sectionBeginTemplate.apply(layout, this));
                }

                var provider = current['provider'] || getValue,
                    property = typeof current['property'] == 'string'
                        ? current['property']
                        : current['name'],
                    value = typeof current['value'] === 'undefined'
                        ? provider(entry, property) || entry
                        : current['value'],
                    rendered,
                    formatted;

                if (current['template'] || current['tpl'])
                {
                    rendered = (current['template'] || current['tpl']).apply(value, this);
                    formatted = current['encode'] === true
                        ? encodeValue(rendered)
                        : rendered;
                }
                else if (current['renderer'] && typeof current['renderer'] === 'function')
                {
                    rendered = current['renderer'].call(this, value);
                    formatted = current['encode'] === true
                        ? encodeValue(rendered)
                        : rendered;
                }
                else
                {
                    formatted = current['encode'] !== false
                        ? encodeValue(value)
                        : value;
                }

                var data = dojo.mixin({}, {
                    entry: entry,
                    value: formatted,
                    raw: value
                }, current);

                if (current['descriptor'])
                    data['descriptor'] = typeof current['descriptor'] === 'function'
                        ? this.expandExpression(current['descriptor'], entry, value)
                        : provider(entry, current['descriptor']);

                if (current['action'])
                    data['action'] = this.expandExpression(current['action'], entry, value);

                var hasAccess = App.hasAccessTo(current['security']);
                if (current['security'])
                    data['disabled'] = !hasAccess;

                if (current['disabled'] && hasAccess)
                    data['disabled'] = this.expandExpression(current['disabled'], entry, value);

                if (current['view']) {
                    var context = {};
                    if (current['key'])
                        context['key'] = typeof current['key'] === 'function'
                            ? this.expandExpression(current['key'], entry)
                            : provider(entry, current['key']);
                    if (current['where'])
                        context['where'] = this.expandExpression(current['where'], entry);
                    if (current['resourceKind'])
                        context['resourceKind'] = this.expandExpression(current['resourceKind'], entry);
                    if (current['resourceProperty'])
                        context['resourceProperty'] = this.expandExpression(current['resourceProperty'], entry);
                    if (current['resourcePredicate'])
                        context['resourcePredicate'] = this.expandExpression(current['resourcePredicate'], entry);

                    data['view'] = current['view'];
                    data['context'] = dojo.toJson(context);
                }

                // priority: wrap > (relatedPropertyTemplate | relatedTemplate) > (actionPropertyTemplate | actionTemplate) > propertyTemplate
                var useListTemplate = (layout['list'] || options['list']),
                    template = current['use']
                        ? current['use']
                        : current['view']
                            ? useListTemplate
                                ? this.relatedTemplate
                                : this.relatedPropertyTemplate
                            : current['action']
                                ? useListTemplate
                                    ? this.actionTemplate
                                    : this.actionPropertyTemplate
                                : this.propertyTemplate;

                content.push(template.apply(data, this));
            }

            if (sectionStarted) content.push(this.sectionEndTemplate.apply(layout, this));

            dojo.query(this.contentNode).append(content.join(''));

            for (var i = 0; i < sectionQueue.length; i++)
            {
                var current = sectionQueue[i];

                this.processLayout(current, entry);
            }
        },
        processEntry: function(entry) {
            this.entry = entry;

            if (this.entry)
            {
                this.processLayout(this._createCustomizedLayout(this.createLayout()), this.entry);
            }
            else
            {
                this.set('detailContent', '');
            }
        },
        onRequestDataFailure: function(response, o) {
            if (response && response.status == 404)
                dojo.query(this.contentNode).append(this.notAvailableTemplate.apply(this));
            else
                alert(dojo.string.substitute(this.requestErrorText, [response, o]));

            dojo.removeClass(this.domNode, 'panel-loading');
        },
        onRequestDataAborted: function(response, o) {
            this.options = false; // force a refresh
            dojo.removeClass(this.domNode, 'panel-loading');
        },
        onRequestDataSuccess: function(entry) {
            this.processEntry(entry);
            dojo.removeClass(this.domNode, 'panel-loading');
        },
        requestData: function() {
            dojo.addClass(this.domNode, 'panel-loading');

            var request = this.createRequest();
            if (request)
                request.read({
                    success: this.onRequestDataSuccess,
                    failure: this.onRequestDataFailure,
                    aborted: this.onRequestDataAborted,
                    scope: this
                });
        },
        refreshRequiredFor: function(options) {
            if (this.options) {
                if (options) {
                    if (this.options.key !== options.key) return true;
                }
                return false;
            }
            else
                return this.inherited(arguments);
        },
        activate: function(tag, data) {
            var options = data && data.options;
            if (options && options.descriptor)
                options.title = options.title || options.descriptor;

            this.inherited(arguments);
        },
        show: function(options) {
            if (options && options.descriptor)
                options.title = options.title || options.descriptor;

            this.inherited(arguments);
        },
        getTag: function() {
            return this.options && this.options.key;
        },
        getContext: function() {
            return dojo.mixin(this.inherited(arguments), {
                resourceKind: this.resourceKind,
                key: this.options.key,
                descriptor: this.options.descriptor
            });
        },
        beforeTransitionTo: function() {
            this.inherited(arguments);

            this.canEdit = this.editor ? true : false;

            if (this.refreshRequired)
            {
                this.clear();
            }
        },
        refresh: function() {
            if (this.security && !App.hasAccessTo(this.expandExpression(this.security)))
            {
                dojo.query(this.contentNode).append(this.notAvailableTemplate.apply(this));
                return;
            }

            this.requestData();
        },
        transitionTo: function() {
            this.inherited(arguments);
        },
        clear: function() {
            this.set('detailContent', this.emptyTemplate.apply(this));
            this.context = false;
        }
    });
});
<|endoftext|>


<commit_before>import * as got from 'got';

class Dialex {
  key: string;
  url: string;

  constructor(key: string) {
    this.key = key;
    this.url = 'https://dialexherok.herokuapp.com';
  }

  transform(transformContent: string, lang: string): Promise<any> {
    const self = this;

    return new Promise((resolve, reject) => {
      got(`${self.url}/api/v1/process`, {
        query: {
          apikey: self.key,
          data: transformContent,
          lang: lang
        }})
        .then(response => resolve(response.body))
        .catch(err => reject(err.message || err));
    });
  }
}

export default Dialex;
<commit_msg>Update Dialex class to use axios<commit_after>import axios from 'axios';

class Dialex {
  key: string;
  url: string;

  constructor(key: string) {
    this.key = key;
    this.url = 'https://dialexherok.herokuapp.com';
  }

  transform(transformContent: string, lang: string): Promise<any> {
    const self = this;

    return new Promise((resolve, reject) => {
      axios.get(`${self.url}/api/v1/process`, {
        params: {
          apikey: self.key,
          data: transformContent,
          lang: lang
        }})
        .then(response => resolve(response.data.output.result))
        .catch(err => reject(err.message || err));
    });
  }
}

export default Dialex;
<|endoftext|>


<commit_before>b70b5ed4-973a-11e6-b514-8086f2004150<commit_msg>this is my first commit?<commit_after>b70bf1d2-973a-11e6-b514-8086f2004150<|endoftext|>


<commit_before>VIE.prototype.Entity = function(attrs, opts) {

    attrs = (attrs)? attrs : {};
    opts = (opts)? opts : {};

    var self = this;

    if (attrs['@type'] !== undefined) {
        attrs['@type'] = (_.isArray(attrs['@type']))? attrs['@type'] : [ attrs['@type'] ];
        attrs['@type'] = _.map(attrs['@type'], function(val){
            if (!self.vie.types.get(val)) {
                //if there is no such type -> add it and let it inherit from "owl:Thing"
                self.vie.types.add(val).inherit("owl:Thing");
            }
            return self.vie.types.get(val).id;
        });
        attrs['@type'] = (attrs['@type'].length === 1)? attrs['@type'][0] : attrs['@type'];
    } else {
        // provide "owl:Thing" as the default type if none was given
        attrs['@type'] = self.vie.types.get("owl:Thing").id;
    }

    //the following provides full seamless namespace support
    //for attributes. It should not matter, if you
    //query for `model.get('name')` or `model.get('foaf:name')`
    //or even `model.get('http://xmlns.com/foaf/0.1/name');`
    //However, if we just overwrite `set()` and `get()`, this
    //raises a lot of side effects, so we need to expand
    //the attributes before we create the model.
    _.each (attrs, function (value, key) {
        var newKey = VIE.Util.mapAttributeNS(key, this.namespaces);
        if (key !== newKey) {
            delete attrs[key];
            attrs[newKey] = value;
        }
    }, self.vie);

    var Model = Backbone.Model.extend({
        idAttribute: '@subject',

        initialize: function(attributes, options) {
            if (attributes['@subject']) {
                this.id = this['@subject'] = this.toReference(attributes['@subject']);
            } else {
                this.id = this['@subject'] = attributes['@subject'] = this.cid.replace('c', '_:bnode');
            }
            return this;
        },

        get: function (attr) {
            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
            var value = Backbone.Model.prototype.get.call(this, attr);
            value = (_.isArray(value))? value : [ value ];

            value = _.map(value, function(v) {
                if (v !== undefined && attr === '@type' && self.vie.types.get(v)) {
                    return self.vie.types.get(v);
                } else if (v !== undefined && self.vie.entities.get(v)) {
                    return self.vie.entities.get(v);
                } else {
                    return v;
                }
            }, this);
            if(value.length === 0) {
                return undefined;
            }
            // if there is only one element, just return that one
            value = (value.length === 1)? value[0] : value;
            return value;
        },

        has: function(attr) {
            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
            return Backbone.Model.prototype.has.call(this, attr);
        },

        set : function(attrs, options, opts) {

            if (!attrs) {
                return this;
            }

            if (typeof attrs === "string") {
                var obj = {};
                obj[attrs] = options;
                return this.set(obj, opts);
            }
            if (attrs.attributes) {
                attrs = attrs.attributes;
            }
            var self = this;
            _.each (attrs, function (value, key) {
                var newKey = VIE.Util.mapAttributeNS(key, self.vie.namespaces);
                if (key !== newKey) {
                    delete attrs[key];
                    attrs[newKey] = value;
                }
            }, this);
            _.each (attrs, function (value, key) {
               if (!value) { return; }
               if (key.indexOf('@') === -1) {
                   if (value.isCollection) {
                       // ignore
                       value.each(function (child) {
                           self.vie.entities.addOrUpdate(child);
                       });
                   } else if (value.isEntity) {
                       self.vie.entities.addOrUpdate(value);
                       var coll = new self.vie.Collection();
                       coll.add(value);
                       attrs[key] = coll;
                   } else if (_.isArray(value)) {
                       // ignore
                   } else if (typeof value == "object") {
                       var child = new self.vie.Entity(value, options);
                       self.vie.entities.addOrUpdate(child);
                       var coll = new self.vie.Collection();
                       coll.add(value);
                       attrs[key] = coll;
                   } else {
                       // ignore
                   }
               }
            }, this);
            return Backbone.Model.prototype.set.call(this, attrs, options);
        },

        unset: function (attr, opts) {
            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
            return Backbone.Model.prototype.unset.call(this, attr, opts);
        },

        getSubject: function(){
            if (typeof this.id === "undefined") {
                this.id = this.attributes[this.idAttribute];
            }
            if (typeof this.id === 'string') {
                if (this.id.substr(0, 7) === 'http://' || this.id.substr(0, 4) === 'urn:') {
                    return this.toReference(this.id);
                }
                return this.id;
            }
            return this.cid.replace('c', '_:bnode');
        },

        getSubjectUri: function(){
            return this.fromReference(this.getSubject());
        },

        toReference: function(uri){
            var ns = this.vie.namespaces;
            var ret = uri;
            if (uri.substring(0, 2) === "_:") {
                ret = uri;
            }
            else if (ns.isCurie(uri)) {
                ret = ns.uri(uri);
                if (ret === "<" + ns.base() + uri + ">") {
                    /* no base namespace extension with IDs */
                    ret = '<' + uri + '>';
                }
            } else if (!ns.isUri(uri)) {
                ret = '<' + uri + '>';
            }
            return ret;
        },

        fromReference: function(uri){
            var ns = this.vie.namespaces;
            if (!ns.isUri(uri)) {
                return uri;
            }
            return uri.substring(1, uri.length - 1);
        },

        as: function(encoding){
            if (encoding === "JSON") {
                return this.toJSON();
            }
            if (encoding === "JSONLD") {
                return this.toJSONLD();
            }
            throw new Error("Unknown encoding " + encoding);
        },

        toJSONLD: function(){
            var instanceLD = {};
            var instance = this;
            _.each(instance.attributes, function(value, name){
                var entityValue = value; //instance.get(name);

                if (value instanceof instance.vie.Collection) {
                    entityValue = value.map(function(instance) {
                        return instance.getSubject();
                    });
                }

                // TODO: Handle collections separately
                instanceLD[name] = entityValue;
            });

            instanceLD['@subject'] = instance.getSubject();

            return instanceLD;
        },

        setOrAdd: function (arg1, arg2, option) {
            var entity = this;
            if (typeof arg1 === "string" && arg2) {
                // calling entity.setOrAdd("rdfs:type", "example:Musician")
                entity._setOrAddOne(arg1, arg2, option);
            }
            else
                if (typeof arg1 === "object") {
                    // calling entity.setOrAdd({"rdfs:type": "example:Musician", ...})
                    _(arg1).each(function(val, key){
                        entity._setOrAddOne(key, val, arg2);
                    });
                }
            return this;
        },

        /* attr is always of type string */
        /* value can be of type: string,int,double,object,VIE.Entity,VIE.Collection */
       /*  val can be of type: undefined,string,int,double,array,VIE.Collection */

        /* depending on the type of value and the type of val, different actions need to be made */
        _setOrAddOne: function (attr, value, options) {
            if (!attr || !value)
                return;

            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);

            if (_.isArray(value)) {
                for (var v = 0; v < value.length; v++) {
                    this._setOrAddOne(attr, value[v], options);
                }
                return;
            }

            if (attr === "@type" && value instanceof self.vie.Type) {
            	value = value.id;
            }

            var obj = {};
            var existing = Backbone.Model.prototype.get.call(this, attr);

            if (!existing) {
                obj[attr] = value;
                this.set(obj, options);
            } else if (existing.isCollection) {
                if (value.isCollection) {
                    value.each(function (model) {
                        existing.add(model);
                    });
                } else if (value.isEntity) {
                    existing.add(value);
                } else if (typeof value === "object") {
                    value = new this.vie.Entity(value);
                    existing.add(value);
                } else {
                    throw new Error("you cannot add a literal to a collection of entities!");
                }
                this.trigger('change:' + attr, this, value, {});
                this.change({});
            } else if (_.isArray(existing)) {
                if (value.isCollection) {
                	for (var v = 0; v < value.size(); v++) {
                		this._setOrAddOne(attr, value.at(v).getSubject(), options);
                	}
                    /*throw new Error("you cannot add a collection of entities to an array of literals!");*/
                } else if (value.isEntity) {
                	this._setOrAddOne(attr, value.getSubject(), options);
                    /*throw new Error("you cannot add an entity to an array of literals!");*/
                } else if (typeof value === "object") {
                	value = new this.vie.Entity(value);
                	this._setOrAddOne(attr, value, options);
                	/*throw new Error("you cannot add an entity of entities to an array of literals!");*/
                } else {
                    /* yes, we allow multiple equal literals */
                    existing.push(value);
                    obj[attr] = existing;
                    this.set(obj);
                }
            } else {
                var arr = [ existing ];
                arr.push(value);
                obj[attr] = arr;
                this.set(obj, options);
                return this._setOrAddOne(attr, value, options);
            }
        },

        hasType: function(type){
            type = self.vie.types.get(type);
            return this.hasPropertyValue("@type", type);
        },

        hasPropertyValue: function(property, value) {
            var t = this.get(property);
            if (!(value instanceof Object)) {
                value = self.vie.entities.get(value);
            }
            if (t instanceof Array) {
                return t.indexOf(value) !== -1;
            }
            else {
                return t === value;
            }
        },

        isof: function (type) {
            var types = this.get('@type');

            if (types === undefined) {
                return false;
            }
            types = (_.isArray(types))? types : [ types ];

            type = (self.vie.types.get(type))? self.vie.types.get(type) : new self.vie.Type(type);
            for (var t = 0; t < types.length; t++) {
                if (self.vie.types.get(types[t])) {
                    if (self.vie.types.get(types[t]).isof(type)) {
                        return true;
                    }
                } else {
                    var typeTmp = new self.vie.Type(types[t]);
                    if (typeTmp.id === type.id) {
                        return true;
                    }
                }
            }
            return false;
        },

        addTo : function (collection, update) {
            var self = this;
            if (collection instanceof self.vie.Collection) {
                if (update) {
                    collection.addOrUpdate(self);
                } else {
                    collection.add(self);
                }
                return this;
            }
            throw new Error("Please provide a proper collection of type VIE.Collection as argument!");
        },

        isEntity: true,

        vie: self.vie
    });

    return new Model(attrs, opts);
};
<commit_msg>removed comments<commit_after>VIE.prototype.Entity = function(attrs, opts) {

    attrs = (attrs)? attrs : {};
    opts = (opts)? opts : {};

    var self = this;

    if (attrs['@type'] !== undefined) {
        attrs['@type'] = (_.isArray(attrs['@type']))? attrs['@type'] : [ attrs['@type'] ];
        attrs['@type'] = _.map(attrs['@type'], function(val){
            if (!self.vie.types.get(val)) {
                //if there is no such type -> add it and let it inherit from "owl:Thing"
                self.vie.types.add(val).inherit("owl:Thing");
            }
            return self.vie.types.get(val).id;
        });
        attrs['@type'] = (attrs['@type'].length === 1)? attrs['@type'][0] : attrs['@type'];
    } else {
        // provide "owl:Thing" as the default type if none was given
        attrs['@type'] = self.vie.types.get("owl:Thing").id;
    }

    //the following provides full seamless namespace support
    //for attributes. It should not matter, if you
    //query for `model.get('name')` or `model.get('foaf:name')`
    //or even `model.get('http://xmlns.com/foaf/0.1/name');`
    //However, if we just overwrite `set()` and `get()`, this
    //raises a lot of side effects, so we need to expand
    //the attributes before we create the model.
    _.each (attrs, function (value, key) {
        var newKey = VIE.Util.mapAttributeNS(key, this.namespaces);
        if (key !== newKey) {
            delete attrs[key];
            attrs[newKey] = value;
        }
    }, self.vie);

    var Model = Backbone.Model.extend({
        idAttribute: '@subject',

        initialize: function(attributes, options) {
            if (attributes['@subject']) {
                this.id = this['@subject'] = this.toReference(attributes['@subject']);
            } else {
                this.id = this['@subject'] = attributes['@subject'] = this.cid.replace('c', '_:bnode');
            }
            return this;
        },

        get: function (attr) {
            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
            var value = Backbone.Model.prototype.get.call(this, attr);
            value = (_.isArray(value))? value : [ value ];

            value = _.map(value, function(v) {
                if (v !== undefined && attr === '@type' && self.vie.types.get(v)) {
                    return self.vie.types.get(v);
                } else if (v !== undefined && self.vie.entities.get(v)) {
                    return self.vie.entities.get(v);
                } else {
                    return v;
                }
            }, this);
            if(value.length === 0) {
                return undefined;
            }
            // if there is only one element, just return that one
            value = (value.length === 1)? value[0] : value;
            return value;
        },

        has: function(attr) {
            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
            return Backbone.Model.prototype.has.call(this, attr);
        },

        set : function(attrs, options, opts) {

            if (!attrs) {
                return this;
            }

            if (typeof attrs === "string") {
                var obj = {};
                obj[attrs] = options;
                return this.set(obj, opts);
            }
            if (attrs.attributes) {
                attrs = attrs.attributes;
            }
            var self = this;
            _.each (attrs, function (value, key) {
                var newKey = VIE.Util.mapAttributeNS(key, self.vie.namespaces);
                if (key !== newKey) {
                    delete attrs[key];
                    attrs[newKey] = value;
                }
            }, this);
            _.each (attrs, function (value, key) {
               if (!value) { return; }
               if (key.indexOf('@') === -1) {
                   if (value.isCollection) {
                       // ignore
                       value.each(function (child) {
                           self.vie.entities.addOrUpdate(child);
                       });
                   } else if (value.isEntity) {
                       self.vie.entities.addOrUpdate(value);
                       var coll = new self.vie.Collection();
                       coll.add(value);
                       attrs[key] = coll;
                   } else if (_.isArray(value)) {
                       // ignore
                   } else if (typeof value == "object") {
                       var child = new self.vie.Entity(value, options);
                       self.vie.entities.addOrUpdate(child);
                       var coll = new self.vie.Collection();
                       coll.add(value);
                       attrs[key] = coll;
                   } else {
                       // ignore
                   }
               }
            }, this);
            return Backbone.Model.prototype.set.call(this, attrs, options);
        },

        unset: function (attr, opts) {
            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
            return Backbone.Model.prototype.unset.call(this, attr, opts);
        },

        getSubject: function(){
            if (typeof this.id === "undefined") {
                this.id = this.attributes[this.idAttribute];
            }
            if (typeof this.id === 'string') {
                if (this.id.substr(0, 7) === 'http://' || this.id.substr(0, 4) === 'urn:') {
                    return this.toReference(this.id);
                }
                return this.id;
            }
            return this.cid.replace('c', '_:bnode');
        },

        getSubjectUri: function(){
            return this.fromReference(this.getSubject());
        },

        toReference: function(uri){
            var ns = this.vie.namespaces;
            var ret = uri;
            if (uri.substring(0, 2) === "_:") {
                ret = uri;
            }
            else if (ns.isCurie(uri)) {
                ret = ns.uri(uri);
                if (ret === "<" + ns.base() + uri + ">") {
                    /* no base namespace extension with IDs */
                    ret = '<' + uri + '>';
                }
            } else if (!ns.isUri(uri)) {
                ret = '<' + uri + '>';
            }
            return ret;
        },

        fromReference: function(uri){
            var ns = this.vie.namespaces;
            if (!ns.isUri(uri)) {
                return uri;
            }
            return uri.substring(1, uri.length - 1);
        },

        as: function(encoding){
            if (encoding === "JSON") {
                return this.toJSON();
            }
            if (encoding === "JSONLD") {
                return this.toJSONLD();
            }
            throw new Error("Unknown encoding " + encoding);
        },

        toJSONLD: function(){
            var instanceLD = {};
            var instance = this;
            _.each(instance.attributes, function(value, name){
                var entityValue = value; //instance.get(name);

                if (value instanceof instance.vie.Collection) {
                    entityValue = value.map(function(instance) {
                        return instance.getSubject();
                    });
                }

                // TODO: Handle collections separately
                instanceLD[name] = entityValue;
            });

            instanceLD['@subject'] = instance.getSubject();

            return instanceLD;
        },

        setOrAdd: function (arg1, arg2, option) {
            var entity = this;
            if (typeof arg1 === "string" && arg2) {
                // calling entity.setOrAdd("rdfs:type", "example:Musician")
                entity._setOrAddOne(arg1, arg2, option);
            }
            else
                if (typeof arg1 === "object") {
                    // calling entity.setOrAdd({"rdfs:type": "example:Musician", ...})
                    _(arg1).each(function(val, key){
                        entity._setOrAddOne(key, val, arg2);
                    });
                }
            return this;
        },

        /* attr is always of type string */
        /* value can be of type: string,int,double,object,VIE.Entity,VIE.Collection */
       /*  val can be of type: undefined,string,int,double,array,VIE.Collection */

        /* depending on the type of value and the type of val, different actions need to be made */
        _setOrAddOne: function (attr, value, options) {
            if (!attr || !value)
                return;

            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);

            if (_.isArray(value)) {
                for (var v = 0; v < value.length; v++) {
                    this._setOrAddOne(attr, value[v], options);
                }
                return;
            }

            if (attr === "@type" && value instanceof self.vie.Type) {
            	value = value.id;
            }

            var obj = {};
            var existing = Backbone.Model.prototype.get.call(this, attr);

            if (!existing) {
                obj[attr] = value;
                this.set(obj, options);
            } else if (existing.isCollection) {
                if (value.isCollection) {
                    value.each(function (model) {
                        existing.add(model);
                    });
                } else if (value.isEntity) {
                    existing.add(value);
                } else if (typeof value === "object") {
                    value = new this.vie.Entity(value);
                    existing.add(value);
                } else {
                    throw new Error("you cannot add a literal to a collection of entities!");
                }
                this.trigger('change:' + attr, this, value, {});
                this.change({});
            } else if (_.isArray(existing)) {
                if (value.isCollection) {
                	for (var v = 0; v < value.size(); v++) {
                		this._setOrAddOne(attr, value.at(v).getSubject(), options);
                	}
                } else if (value.isEntity) {
                	this._setOrAddOne(attr, value.getSubject(), options);
                } else if (typeof value === "object") {
                	value = new this.vie.Entity(value);
                	this._setOrAddOne(attr, value, options);
                } else {
                    /* yes, we (have to) allow multiple equal values */
                    existing.push(value);
                    obj[attr] = existing;
                    this.set(obj);
                }
            } else {
                var arr = [ existing ];
                arr.push(value);
                obj[attr] = arr;
                this.set(obj, options);
                return this._setOrAddOne(attr, value, options);
            }
        },

        hasType: function(type){
            type = self.vie.types.get(type);
            return this.hasPropertyValue("@type", type);
        },

        hasPropertyValue: function(property, value) {
            var t = this.get(property);
            if (!(value instanceof Object)) {
                value = self.vie.entities.get(value);
            }
            if (t instanceof Array) {
                return t.indexOf(value) !== -1;
            }
            else {
                return t === value;
            }
        },

        isof: function (type) {
            var types = this.get('@type');

            if (types === undefined) {
                return false;
            }
            types = (_.isArray(types))? types : [ types ];

            type = (self.vie.types.get(type))? self.vie.types.get(type) : new self.vie.Type(type);
            for (var t = 0; t < types.length; t++) {
                if (self.vie.types.get(types[t])) {
                    if (self.vie.types.get(types[t]).isof(type)) {
                        return true;
                    }
                } else {
                    var typeTmp = new self.vie.Type(types[t]);
                    if (typeTmp.id === type.id) {
                        return true;
                    }
                }
            }
            return false;
        },

        addTo : function (collection, update) {
            var self = this;
            if (collection instanceof self.vie.Collection) {
                if (update) {
                    collection.addOrUpdate(self);
                } else {
                    collection.add(self);
                }
                return this;
            }
            throw new Error("Please provide a proper collection of type VIE.Collection as argument!");
        },

        isEntity: true,

        vie: self.vie
    });

    return new Model(attrs, opts);
};
<|endoftext|>


<commit_before>var editor
var editorDirty = false
var output
var status = 'nothing' // reflects what status the buttons show: 'running' or 'nothing'
var state // various things shared with share.js, including group consideration of the running status

// Display whether we have unsaved edits
var update_dirty = function(value) {
  clearTimeout(save_code)
  editorDirty = value
  if (editorDirty) {
    // Wait three seconds and then save. If we get another change
    // in those three seconds, reset that timer to avoid excess saves.
    $("#saved").text("Saving...")
    setTimeout(save_code, 3000)
  } else {
    $("#saved").text("All changes saved")
  }
}

// Prevent navigation away if not saved
// XXX this doesn't seem to work for close events in chrome in the frame
$(window).on('beforeunload', function() {
  if (editorDirty) {
    return "You've made changes that haven't been saved yet."
  }
})

// Work out language we're in from the shebang line
var set_editor_mode = function(code) {
  var first = code.split("\n")[0]
  if (first.substr(0,2) != "#!") {
    scraperwiki.alert("Specify language in the first line!", "For example, put #!/usr/bin/ruby, #!/usr/bin/R or #!/usr/bin/python.", false)
    return false
  }
  // Please add more as you need them and send us a pull request!
  if (first.indexOf("python") != -1) {
    editor.getSession().setMode("ace/mode/python")
  } else if (first.indexOf("ruby") != -1) {
    editor.getSession().setMode("ace/mode/ruby")
  } else if (first.indexOf("perl") != -1) {
    editor.getSession().setMode("ace/mode/perl")
  } else if (first.indexOf("node") != -1) {
    editor.getSession().setMode("ace/mode/javascript")
  } else if (first.indexOf("coffee") != -1) {
    editor.getSession().setMode("ace/mode/coffee")
  } else if (first.indexOf("clojure") != -1) {
    editor.getSession().setMode("ace/mode/clojure")
  } else if (first.indexOf("R") != -1) {
    editor.getSession().setMode("ace/mode/r")
  } else if (first.indexOf("sh") != -1) {
    editor.getSession().setMode("ace/mode/sh")
  } else {
    editor.getSession().setMode("ace/mode/text")
  }
  return true
}

// Got a new state over ShareJS (from ourselves or remote clients)
var shared_state_update = function(op) {
  console.log("shared_state_update", state.snapshot)

  // Respond to the status change
  var new_status = state.snapshot.status
  if (new_status != status) {
    console.log("status change", status, "===>", new_status)
    if (new_status == "running") {
      poll_output()
    }
    status = new_status
  }
  update_display_from_status(new_status)
}

// Show status in buttons - we have this as we can call it directly
// to make the run button seem more responsive
var update_display_from_status = function(use_status) {
  if (use_status == "running") {
    $("#run").text("Running...").removeClass("btn-primary").addClass("btn-warning")
  } else if (use_status == "nothing") {
    $("#run").text("Run!").removeClass("btn-warning").addClass("btn-primary")
  }
}

// Show/hide things according to current state
var set_status = function(new_status) {
  if (new_status != "running" && new_status != "nothing") {
    scraperwiki.alert("Unknown new status!", new_status, true)
    return
  }

  // Tell other ShareJS clients the status has changed
  state.submitOp( {p:['status'],od:status,oi:new_status}) // XXX state.status for od?
}

// Console get more output
var poll_output = function() {
  scraperwiki.exec("./tool/enrunerate", function(text) {
    console.log("enrunerate:", text, "len:", text.length)
    set_status(text)

    // we poll one last time either way to be sure we get end of output...
    var again = false
    if (text == "running") {
      // ... but if script is still "running" we'll trigger the timer to do it again
      again = true
    }
    scraperwiki.exec("cat logs/out", function(text) {
      // XXX detect no file a better way
      if (text != "cat: logs/out: No such file or directory\n") {
        output.setValue(text)
      }
      output.clearSelection()
      if (again) {
        setTimeout(poll_output, 10)
      }
    }, function(jqXHR, textStatus, errorThrown) {
        scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
    })
  }, function(jqXHR, textStatus, errorThrown) {
      scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
  })
}

// Clear any errors
var clear_alerts = function() {
  $(".alert").remove()
}

// Save the code - optionally takes text of extra commands to also
// in the same "exec" and a callback to run when done
var save_code = function(extraCmds, callback) {
  clearTimeout(save_code) // stop any already scheduled timed saves
  var code = editor.getValue()
  if (code.length == 0 || code.charAt(code.length - 1) != "\n") {
    code += "\n" // we need a separator \n at the end of the file for the ENDOFSCRAPER heredoc below
  }
  var cmd = "cat >code/scraper.new.$$ <<ENDOFSCRAPER\n" + code + "ENDOFSCRAPER\n"
  cmd = cmd + "chmod a+x code/scraper.new.$$; mv code/scraper.new.$$ code/scraper; " + extraCmds
  scraperwiki.exec(cmd, function(text) {
    // Check actual content against saved - in case there was a change while we executed
    if (editor.getValue() == code) {
      console.log("Saved fine without intereference")
      update_dirty(false)
    } else {
      console.log("Ooops, it got dirty while saving")
      update_dirty(true)
    }

    if (callback) {
      callback(text)
    }
  }, function(jqXHR, textStatus, errorThrown) {
    scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
  })
}

// When the "documentation" button is pressed
var do_docs = function() {
  window.open("https://scraperwiki.com/docs/", "_blank")
}

// When the "bugs" button is pressed
var do_bugs = function() {
  window.open("https://github.com/frabcus/code-scraper-in-browser-tool/issues", "_blank")
}

// When the "keys" button is pressed
var do_keys = function() {
  window.open("https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts", "_blank")
}

// When the "run" button is pressed
var do_run = function() {
  // force a check that we have a shebang (#!) line
  clear_alerts()
  var code = editor.getValue()
  if (!set_editor_mode(code)) {
    return
  }

  // to make button feel responsibe, temporarily show the wrong status
  // (until next operation makes it right)
  update_display_from_status("running")
  output.setValue("")

  // Save code, run it
  save_code("./tool/enrunerate run", function (text) {
    console.log("enrunerate run:", text)
    // And tell all clients that we're now running code (if we are!)
    set_status(text)
  })
}

// Main entry point
$(document).ready(function() {
  settings = scraperwiki.readSettings()
  $('#apikey').val(settings.source.apikey)

  async.auto({
    // Load code from file
    load_code: function(callback) {
      console.log("loading...")
      scraperwiki.exec('mkdir -p code; touch code/scraper; cat code/scraper', function(data) {
        // If nothing there, set some default content to get people going
        if (data.match(/^\s*$/)) {
          data = "#!/usr/bin/python\n\nimport scraperwiki\n\n"
        }
        callback(null, data)
      }, function(jqXHR, textStatus, errorThrown) {
         scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
         callback(true, null)
      })
    },
    // Connect to sharejs
    sharejs_connection: function(callback) {
      console.log("connecting...")
      connection = new sharejs.Connection('http://seagrass.goatchurch.org.uk/sharejs/channel')
      callback(null, connection)
    },
    // Wire up shared document on the connection
    share_doc: ['sharejs_connection', function(callback, results) {
      console.log("sharing doc...")
      // XXX need a better token in here. API key?
      var docName = 'scraperwiki-' + scraperwiki.box + '-doc001'
      results.sharejs_connection.open(docName, 'text', function(error, doc) {
        if (error) {
          scraperwiki.alert("Trouble setting up pair editing!", error, true)
          callback(true, null)
        }
        callback(null, doc)
      })
    }],
    share_state: ['sharejs_connection', function(callback, results) {
      console.log("sharing state...")
      // XXX need a better token in here. API key?
      var docName = 'scraperwiki-' + scraperwiki.box + '-state001'
      results.sharejs_connection.open(docName, 'json', function(error, doc) {
        if (error) {
          scraperwiki.alert("Trouble setting up pair state!", error, true)
          callback(true, null)
        }
        callback(null, doc)
      })
    }]
   }, function(err, results) {
      if (err) {
        return
      }
      var data = results.load_code
      var doc = results.share_doc
      state = results.share_state

      // Start to share status
      if (state.created) {
        console.log("first time this state connection has been used, initialising")
        state.submitOp([{p:[],od:null,oi:{status:'nothing'}}])
      }
      state.on('change', function (op) {
        shared_state_update(op)
      })

      // Create editor window
      editor = ace.edit("editor")
      editor.getSession().setUseSoftTabs(true)
      editor.setTheme("ace/theme/monokai")
      set_editor_mode(data)
      doc.attach_ace(editor)
      editor.setValue(data) // XXX this overrides what is in filesystem on top of what is in sharej
      editor.moveCursorTo(0, 0)
      editor.focus()

      update_dirty(false)
      editor.on('change', function() {
        update_dirty(true)
      })

      poll_output()
   });

  // Create the console output window
  output = ace.edit("output")
  output.setTheme("ace/theme/monokai")
  // ... we use /bin/sh syntax highlighting, the only other at all
  // credible option for such varied output is plain text, which is dull.
  output.getSession().setMode("ace/mode/sh")

  // Bind all the buttons to do something
  $('#docs').on('click', do_docs)
  $('#bugs').on('click', do_bugs)
  $('#keys').on('click', do_keys)
  $('#run').on('click', do_run)
  $(document).bind('keydown', 'ctrl+r', do_run)
})

<commit_msg>Stop button<commit_after>var editor
var editorDirty = false
var output
var status = 'nothing' // reflects what status the buttons show: 'running' or 'nothing'
var changing ='' // for starting/stopping states
var state // various things shared with share.js, including group consideration of the running status

// Display whether we have unsaved edits
var update_dirty = function(value) {
  clearTimeout(save_code)
  editorDirty = value
  if (editorDirty) {
    // Wait three seconds and then save. If we get another change
    // in those three seconds, reset that timer to avoid excess saves.
    $("#saved").text("Saving...")
    setTimeout(save_code, 3000)
  } else {
    $("#saved").text("All changes saved")
  }
}

// Prevent navigation away if not saved
// XXX this doesn't seem to work for close events in chrome in the frame
$(window).on('beforeunload', function() {
  if (editorDirty) {
    return "You've made changes that haven't been saved yet."
  }
})

// Work out language we're in from the shebang line
var set_editor_mode = function(code) {
  var first = code.split("\n")[0]
  if (first.substr(0,2) != "#!") {
    scraperwiki.alert("Specify language in the first line!", "For example, put #!/usr/bin/ruby, #!/usr/bin/R or #!/usr/bin/python.", false)
    return false
  }
  // Please add more as you need them and send us a pull request!
  if (first.indexOf("python") != -1) {
    editor.getSession().setMode("ace/mode/python")
  } else if (first.indexOf("ruby") != -1) {
    editor.getSession().setMode("ace/mode/ruby")
  } else if (first.indexOf("perl") != -1) {
    editor.getSession().setMode("ace/mode/perl")
  } else if (first.indexOf("node") != -1) {
    editor.getSession().setMode("ace/mode/javascript")
  } else if (first.indexOf("coffee") != -1) {
    editor.getSession().setMode("ace/mode/coffee")
  } else if (first.indexOf("clojure") != -1) {
    editor.getSession().setMode("ace/mode/clojure")
  } else if (first.indexOf("R") != -1) {
    editor.getSession().setMode("ace/mode/r")
  } else if (first.indexOf("sh") != -1) {
    editor.getSession().setMode("ace/mode/sh")
  } else {
    editor.getSession().setMode("ace/mode/text")
  }
  return true
}

// Got a new state over ShareJS (from ourselves or remote clients)
var shared_state_update = function(op) {
  console.log("shared_state_update", state.snapshot)

  // Respond to the status change
  var new_status = state.snapshot.status
  if (new_status != status) {
    console.log("status change", status, "===>", new_status)
    if (new_status == "running") {
      poll_output()
    }
    status = new_status
    changing = ""
  }
  update_display_from_status(new_status)
}

// Show status in buttons - we have this as we can call it directly
// to make the run button seem more responsive
var update_display_from_status = function(use_status) {
  $("#run").removeClass("btn-primary").removeClass("btn-danger").removeClass("btn-warning").removeClass("btn-success")
  if (changing == "starting") {
    $("#run").text("Starting...").addClass("btn-warning")
  } else if (changing == "stopping") {
    $("#run").text("Stopping...").addClass("btn-danger")
  } else if (use_status == "running") {
    $("#run").text("Running...").addClass("btn-success")
  } else if (use_status == "nothing") {
    $("#run").text("Run!").addClass("btn-primary")
  }
}

// Show/hide things according to current state
var set_status = function(new_status) {
  if (new_status != "running" && new_status != "nothing") {
    scraperwiki.alert("Unknown new status!", new_status, true)
    return
  }

  // Tell other ShareJS clients the status has changed
  state.submitOp( {p:['status'],od:status,oi:new_status}) // XXX state.status for od?
}

// Console get more output
var poll_output = function() {
  scraperwiki.exec("./tool/enrunerate", function(text) {
    console.log("enrunerate:", text, "len:", text.length)
    set_status(text)

    // we poll one last time either way to be sure we get end of output...
    var again = false
    if (text == "running") {
      // ... but if script is still "running" we'll trigger the timer to do it again
      again = true
    }
    scraperwiki.exec("cat logs/out", function(text) {
      // XXX detect no file a better way
      if (text != "cat: logs/out: No such file or directory\n") {
        output.setValue(text)
      }
      output.clearSelection()
      if (again) {
        setTimeout(poll_output, 10)
      }
    }, function(jqXHR, textStatus, errorThrown) {
        scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
    })
  }, function(jqXHR, textStatus, errorThrown) {
      scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
  })
}

// Clear any errors
var clear_alerts = function() {
  $(".alert").remove()
}

// Save the code - optionally takes text of extra commands to also
// in the same "exec" and a callback to run when done
var save_code = function(extraCmds, callback) {
  clearTimeout(save_code) // stop any already scheduled timed saves
  var code = editor.getValue()
  if (code.length == 0 || code.charAt(code.length - 1) != "\n") {
    code += "\n" // we need a separator \n at the end of the file for the ENDOFSCRAPER heredoc below
  }
  var cmd = "cat >code/scraper.new.$$ <<ENDOFSCRAPER\n" + code + "ENDOFSCRAPER\n"
  cmd = cmd + "chmod a+x code/scraper.new.$$; mv code/scraper.new.$$ code/scraper; " + extraCmds
  scraperwiki.exec(cmd, function(text) {
    // Check actual content against saved - in case there was a change while we executed
    if (editor.getValue() == code) {
      console.log("Saved fine without intereference")
      update_dirty(false)
    } else {
      console.log("Ooops, it got dirty while saving")
      update_dirty(true)
    }

    if (callback) {
      callback(text)
    }
  }, function(jqXHR, textStatus, errorThrown) {
    scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
  })
}

// When the "documentation" button is pressed
var do_docs = function() {
  window.open("https://scraperwiki.com/docs/", "_blank")
}

// When the "bugs" button is pressed
var do_bugs = function() {
  window.open("https://github.com/frabcus/code-scraper-in-browser-tool/issues", "_blank")
}

// When the "keys" button is pressed
var do_keys = function() {
  window.open("https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts", "_blank")
}

// When the "run" button is pressed
var do_run = function() {
  // force a check that we have a shebang (#!) line
  clear_alerts()
  var code = editor.getValue()
  if (!set_editor_mode(code)) {
    return
  }

  // if it is already running, stop instead
  if (status == "running") {
    // make button show what we're doing
    changing = "stopping"
    update_display_from_status(status)

    scraperwiki.exec("./tool/enrunerate stop", function(text) {
      console.log("enrunerate stop:", text)
      // And tell all clients that we're now not running code (if we're not!)
      set_status(text)
    })
    return
  }

  // to make button feel responsibe, temporarily show the wrong status
  // (until next operation makes it right)
  changing = "starting"
  update_display_from_status(status)
  output.setValue("")

  // Save code, run it
  save_code("./tool/enrunerate run", function (text) {
    console.log("enrunerate run:", text)
    // And tell all clients that we're now running code (if we are!)
    set_status(text)
  })
}

// Main entry point
$(document).ready(function() {
  settings = scraperwiki.readSettings()
  $('#apikey').val(settings.source.apikey)

  async.auto({
    // Load code from file
    load_code: function(callback) {
      console.log("loading...")
      scraperwiki.exec('mkdir -p code; touch code/scraper; cat code/scraper', function(data) {
        // If nothing there, set some default content to get people going
        if (data.match(/^\s*$/)) {
          data = "#!/usr/bin/python\n\nimport scraperwiki\n\n"
        }
        callback(null, data)
      }, function(jqXHR, textStatus, errorThrown) {
         scraperwiki.alert(errorThrown, jqXHR.responseText, "error")
         callback(true, null)
      })
    },
    // Connect to sharejs
    sharejs_connection: function(callback) {
      console.log("connecting...")
      connection = new sharejs.Connection('http://seagrass.goatchurch.org.uk/sharejs/channel')
      callback(null, connection)
    },
    // Wire up shared document on the connection
    share_doc: ['sharejs_connection', function(callback, results) {
      console.log("sharing doc...")
      // XXX need a better token in here. API key?
      var docName = 'scraperwiki-' + scraperwiki.box + '-doc001'
      results.sharejs_connection.open(docName, 'text', function(error, doc) {
        if (error) {
          scraperwiki.alert("Trouble setting up pair editing!", error, true)
          callback(true, null)
        }
        callback(null, doc)
      })
    }],
    share_state: ['sharejs_connection', function(callback, results) {
      console.log("sharing state...")
      // XXX need a better token in here. API key?
      var docName = 'scraperwiki-' + scraperwiki.box + '-state001'
      results.sharejs_connection.open(docName, 'json', function(error, doc) {
        if (error) {
          scraperwiki.alert("Trouble setting up pair state!", error, true)
          callback(true, null)
        }
        callback(null, doc)
      })
    }]
   }, function(err, results) {
      if (err) {
        return
      }
      var data = results.load_code
      var doc = results.share_doc
      state = results.share_state

      // Start to share status
      if (state.created) {
        console.log("first time this state connection has been used, initialising")
        state.submitOp([{p:[],od:null,oi:{status:'nothing'}}])
      }
      state.on('change', function (op) {
        shared_state_update(op)
      })

      // Create editor window
      editor = ace.edit("editor")
      editor.getSession().setUseSoftTabs(true)
      editor.setTheme("ace/theme/monokai")
      set_editor_mode(data)
      doc.attach_ace(editor)
      editor.setValue(data) // XXX this overrides what is in filesystem on top of what is in sharej
      editor.moveCursorTo(0, 0)
      editor.focus()

      update_dirty(false)
      editor.on('change', function() {
        update_dirty(true)
      })

      poll_output()
   });

  // Create the console output window
  output = ace.edit("output")
  output.setTheme("ace/theme/monokai")
  // ... we use /bin/sh syntax highlighting, the only other at all
  // credible option for such varied output is plain text, which is dull.
  output.getSession().setMode("ace/mode/sh")

  // Bind all the buttons to do something
  $('#docs').on('click', do_docs)
  $('#bugs').on('click', do_bugs)
  $('#keys').on('click', do_keys)
  $('#run').on('click', do_run)
  $(document).bind('keydown', 'ctrl+r', do_run)
})

<|endoftext|>