<commit_before>/**
Copyright 2010 Joel Hockey (joel.hockey@gmail.com).  MIT Licence

JST is a parser / generator / compiler.  It parses templates and generates
JavaScript that can be compiled (eval'ed using JavaScript runtime).

JST ensures that the generated JavaScript code will have the same line numbers
as the input template so that error messages from 'eval' will have
line numbers that match the input template.

Each compiled template produces a JavaScript object that is stored
in JST.templates and contains method 'render(out, cx)'
 * 'out' must be an object that implements 'write(str)'
 * 'cx' is an object that is used as the context to provide
   variables used in the template.
JST provides a helper method 'JST.render(name, cx)' that returns
the rendered template as a string.

The most basic usage is to call:
 * JST.compile(name, template);
 * var output = JST.render(name, cx);

    js> load("jst.js");
    js> var template = "{for each (var name in names)}\nHello ${name}\n{/for}";
    js> JST.compile("hello", template);
    js> print(JST.render("hello", { names: ["John", "Paul", "George", "Ringo"] }));
    Hello John
    Hello Paul
    Hello George
    Hello Ringo

    js>

The code below shows parsing and compiling done separately, and
templates writing to a stream.

    js> var generated = JST.parse("hello", template);
    js> print(generated);
    JST.templates["hello"] = {}; if (!JST.templates["hello"].hasOwnProperty("render")) { JST.templates["hello"].render = function (out, cx) { with (cx) { var forcounter = 0; for each (var name in names) { forcounter++;
    out.write("Hello "); h(name, out); out.write("\n\
    "); } }}};
    js> eval(generated);
    js> var stream = { buf: [], write: function(s) { this.buf.push(s); } }
    js> JST.templates.hello.render(stream, { names: ["John", "Paul", "George", "Ringo"] });
    js> print(stream.buf.join(""));
    Hello John
    Hello Paul
    Hello George
    Hello Ringo

    js>
*/

var JST = {
    templates : {},
    compile: function(name, body) { eval(this.parse(name, body)); },
    render: function(name, cx) {
        var out = { buf: [], write: function(s) { this.buf.push(s); }};
        JST.templates[name].render(out, cx);
        return out.buf.join("");
    },
    get: function(templateName, protoName) {
        if (protoName) {
            var F = function() {}
            F.prototype = this.get(protoName);
            JST.templates[templateName] = new F();
        } else {
            JST.templates[templateName] = JST.templates[templateName] || {};
        }
        return JST.templates[templateName];
    },
    parse : function (name, body) {

        // parser variables
        var line = 1;       // used by parser for error messages
        var linepos = 1;    // used by parser for error messages
        var toks = [];      // parser puts results in here
        var groups;         // used by parser to store regexp matches

        // parser puts tokens in 'toks' array.
        // Each token is an object containing information used by generator
        // Format of token:
        //  - type: newline|value|opentag|closetag|text|comment
        //  - tok: exact matched text, e.g. '{for (var item in items)}'
        //  - value: value of token, e.g. 'for', 'function foo'
        //  - words: value.split(), used only in opentag and closetag,
        //     e.g. ['for','(var','item','in','items)'], ['function', 'foo']

        // parse
        while (body.length > 0) {
            // newline
            if (groups = /^[ \t]*(\r?\n|\r)/.exec(body)) {
                toks.push({type: "newline", tok: groups[0], value: groups[0]});

            // comment
            } else if (groups = /^\s*{\*(.*?)\*}/.exec(body)) {
                toks.push({type: "comment", tok: groups[0], value: groups[1]});
            // value, opentag, closetag
            } else if (groups = /^(\$|\s*){(\/?)([^\r\n{}]+)}/.exec(body)) {
                var type = "opentag";
                if (groups[1] === "$") { type = "value";
                } else if (groups[2] === "/") { type = "closetag"; }
                var value = groups[3].replace(/^\s+|\s+$/g, ""); // trim space
                toks.push({type: type, tok: groups[0],
                    value: value, words: value.split(/\s+/)});

            // text
            } else if ((groups = /^[^\r\n${]+/.exec(body)) ||
                    (groups = /^[^\r\n]+/.exec(body))) {
                toks.push({type: "text", tok: groups[0], value: groups[0]});
            } else {
                // impossible
                throw new Error("Could not parse: " + body);
            }
            body = body.substring(groups[0].length);
        }

        // generator variables
        var tagstack = [];  // used by generator to match open/close tags
        var inEval = false; // gen handles tokens differently in eval-mode
        var inText = false; // similar to 'inEval'
        var textparts = []; // text parts are grouped for better render perf
        var fcount = 0;     // number of functions on tagstack
        // src is output of generator
        // First line of 'src' creates template object within 'JST.templates'
        // If template declares prototype, src[0] is replaced with:
        //   JST.get(name, proto);
        var src = ['JST.get("' + name + '"); '];

        // add specified string to src.
        // first put text parts into single 'out.write' statement
        var addsrc = function(s) {
            // first push text onto src
            if (textparts.length) {
                src.push('out.write("' + textparts.join('')
                        .replace(/\r?\n|\r|"/g, function(str) {
                            switch(str) {
                            case "\r\n": return "\\r\\n\\\n";
                            case '"': return '\\"';
                            case "\n": return "\\n\\\n";
                            case "\r": return "\\r\\\n";
                            }
                        }) + '"); ');
                textparts = [];
            }
            src.push(s);
        };

        // errors during generator
        var error = function(b, desc) {
            if (b) {
                throw new Error(desc + ", line: " + line + "."  + linepos
                        + ", tagstack: [" + tagstack.join(" > ") + "]");
            }
        };

        // Generate JS template that can be eval'ed
        // Take care to match line numbers of generated JS with
        // line numbers of source.  We then rely on JS interpreter to
        // provide good error messages

        // skip blank lines at start
        while (toks.length > 0 && toks[0].type === "newline") {
            toks.shift();
            src.push("\n");
        }

        // either set up 'prototype' or wrap toks with 'function render'
        if (toks.length > 0 && (toks[0].type === "opentag"
                    && toks[0].words[0] === "prototype")) {
                var tok = toks.shift();
                error(tok.words.length !== 2,
                        "invalid prototype tag format: " + tok.tok);
                // create empty prototype if it doesn't yet exist
                src[0] = 'JST.get("' + name + '", "' + tok.words[1] + '"); ';
        } else {
            // wrap with render (front and back)
            toks.unshift({type: "opentag", tok: "{function render}",
                value: "function render", words: ["function", "render"]});
            toks.push({type: "closetag", tok: "{/function render}",
                value: "function render", words: ["function", "render"]});
        }

        for (var i = 0; i < toks.length; i++) {
            var tok = toks[i];
            var toptag = tagstack[tagstack.length - 1];
            // newline
            if (tok.type === "newline") {
                // update line, pos is increased at bottom of for-loop
                line++;
                linepos = 1 - tok.value.length;
                // check if newline is formatting only, or needs to be rendered
                // formatting only if we are in {eval}...{/eval},
                // or if line contains only open/close tags, or if fcount === 0
                var j = i;
                while (j > 0 && /^(opentag|closetag|comment)$/.test(toks[--j].type));
                var tags = j < i-1 && (j === 0 || toks[j].type === "newline");
                if (inEval || tags || fcount === 0) {
                    addsrc("\n"); // formatting only
                } else {
                    textparts.push(tok.value); // needs to be rendered
                }

            // are we at end of {text?}...{/text?} or {eval}...{/eval}
            } else if ((inText || inEval) && tok.type === "closetag"
                    && tok.value === toptag) {
                addsrc("; ");
                // we are now finished 'text' or 'eval' section
                inText = inEval = false;
                tagstack.pop();
            } else if (inText) { // still in text
                textparts.push(tok.tok);
            } else if (inEval) { // still in eval
                addsrc(tok.value);

            // comment
            } else if (tok.type === "comment") {
                // do nothing - ignore comment

            // value substitution
            } else if (tok.type === "value") {
                addsrc('h(' + tok.value + ', out); '); // html-escape

            // open tag
            } else if (tok.type === "opentag") {
                if (tok.words[0] === "function") {
                    error(tok.words.length !== 2,
                            "invalid function tag format: " + tok.tok);
                    error(tok.words[1] === "render" && i !== 0,
                            "function 'render' not allowed");
                    // inner functions are wrapped within
                    // 'if (!JST.templates[name].hasOwnProperty(name)) { ...'
                    if (fcount > 0) {
                        addsrc('if (!JST.templates["' + name
                            + '"].hasOwnProperty("' + tok.words[1] + '")) { ');
                    }
                    addsrc('JST.templates["' + name + '"].' + tok.words[1]
                        + ' = function (out, cx) { with (cx) { ');
                    fcount++;
                    tagstack.push(tok.value); // push 'function <fname>'
                } else if (tok.words[0] === "render") {
                    error(tok.words.length !== 2,
                            "invalid render tag format: " + tok.tok);
                    addsrc('JST.templates["' + tok.words[1] + '"].render(out, cx); ');
                } else if (tok.words[0] === "if") {
                    addsrc(tok.value + " {");
                    tagstack.push("if");
                } else if (tok.words[0].match(/^els?e?(if)?$/)) {
                    error(!toptag || !toptag.match(/^(if|els?e?(if)?)$/),
                            "unexpected else(if)? tag");
                    var ifword = tok.words[0] === "else" ? "" : " if ";
                    addsrc("} else " + ifword
                            + tok.words.slice(1).join(" ") + " {");
                } else if (tok.words[0] === "for") {
                    addsrc("var forcounter = 0; " +
                            tok.value + " { forcounter++; ");
                    tagstack.push("for");
                } else if (tok.words[0] === "forelse") {
                    error(toptag !== "for", "unexpected forelse tag");
                    addsrc("} if (forcounter === 0) { ");
                } else if (tok.words[0] === "eval") {
                    if (tok.words.length === 1) {
                         inEval = true;
                         tagstack.push("eval");
                    } else { // inline eval
                        addsrc(tok.words.slice(1).join(" ") + "; ");
                    }
                } else if (tok.words[0].match(/^text/)) {
                    inText = true;
                    tagstack.push(tok.value);
                } else { // treat as text
                    tok.type = "text";
                    textparts.push(tok.tok);
                }

            // close tag
            } else if (tok.type === "closetag") {
                error(toptag !== tok.value, "unexepcted closetag " + tok.tok);
                addsrc("} ");
                tagstack.pop();
                if (tok.words[0] === "function") {
                    addsrc("};");
                    fcount--;
                    // inner functions are wrapped within
                    // 'if (!JST.templates[name].hasOwnProperty(name)) { ...'
                    // and must invoke self
                    if (fcount > 0) {
                        addsrc(" }; this." + tok.words[1] + "(out, cx); ");
                    }
                }

            // text
            } else if (tok.type === "text") {
                // ignore text outside functions
                if (fcount > 0) {
                    textparts.push(tok.value);
                }

            } else {
                // should not happen
                error(true, "unrecognised token type: " + tok.type);
            }
            linepos += tok.value.length
        }

        return src.join("");
    }
}

// JST relies on global method 'h' for html-escape
var h = h || function(s, out) {
    if (!s) return "";
    var result = s.toString().replace(/[&<>"']/g, function(str) {
       switch (str) {
       case "&": return "&";
       case "<": return "<";
       case ">": return ">";
       case '"': return "&quote;";
       case "'": return "'";
       default: return str;
       }
    });
    if (out) {
        out.write(result);
    } else {
        return result;
    }
}
<commit_msg>fixed &quote to &quot<commit_after>/**
Copyright 2010 Joel Hockey (joel.hockey@gmail.com).  MIT Licence

JST is a parser / generator / compiler.  It parses templates and generates
JavaScript that can be compiled (eval'ed using JavaScript runtime).

JST ensures that the generated JavaScript code will have the same line numbers
as the input template so that error messages from 'eval' will have
line numbers that match the input template.

Each compiled template produces a JavaScript object that is stored
in JST.templates and contains method 'render(out, cx)'
 * 'out' must be an object that implements 'write(str)'
 * 'cx' is an object that is used as the context to provide
   variables used in the template.
JST provides a helper method 'JST.render(name, cx)' that returns
the rendered template as a string.

The most basic usage is to call:
 * JST.compile(name, template);
 * var output = JST.render(name, cx);

    js> load("jst.js");
    js> var template = "{for each (var name in names)}\nHello ${name}\n{/for}";
    js> JST.compile("hello", template);
    js> print(JST.render("hello", { names: ["John", "Paul", "George", "Ringo"] }));
    Hello John
    Hello Paul
    Hello George
    Hello Ringo

    js>

The code below shows parsing and compiling done separately, and
templates writing to a stream.

    js> var generated = JST.parse("hello", template);
    js> print(generated);
    JST.templates["hello"] = {}; if (!JST.templates["hello"].hasOwnProperty("render")) { JST.templates["hello"].render = function (out, cx) { with (cx) { var forcounter = 0; for each (var name in names) { forcounter++;
    out.write("Hello "); h(name, out); out.write("\n\
    "); } }}};
    js> eval(generated);
    js> var stream = { buf: [], write: function(s) { this.buf.push(s); } }
    js> JST.templates.hello.render(stream, { names: ["John", "Paul", "George", "Ringo"] });
    js> print(stream.buf.join(""));
    Hello John
    Hello Paul
    Hello George
    Hello Ringo

    js>
*/

var JST = {
    templates : {},
    compile: function(name, body) { eval(this.parse(name, body)); },
    render: function(name, cx) {
        var out = { buf: [], write: function(s) { this.buf.push(s); }};
        JST.templates[name].render(out, cx);
        return out.buf.join("");
    },
    get: function(templateName, protoName) {
        if (protoName) {
            var F = function() {}
            F.prototype = this.get(protoName);
            JST.templates[templateName] = new F();
        } else {
            JST.templates[templateName] = JST.templates[templateName] || {};
        }
        return JST.templates[templateName];
    },
    parse : function (name, body) {

        // parser variables
        var line = 1;       // used by parser for error messages
        var linepos = 1;    // used by parser for error messages
        var toks = [];      // parser puts results in here
        var groups;         // used by parser to store regexp matches

        // parser puts tokens in 'toks' array.
        // Each token is an object containing information used by generator
        // Format of token:
        //  - type: newline|value|opentag|closetag|text|comment
        //  - tok: exact matched text, e.g. '{for (var item in items)}'
        //  - value: value of token, e.g. 'for', 'function foo'
        //  - words: value.split(), used only in opentag and closetag,
        //     e.g. ['for','(var','item','in','items)'], ['function', 'foo']

        // parse
        while (body.length > 0) {
            // newline
            if (groups = /^[ \t]*(\r?\n|\r)/.exec(body)) {
                toks.push({type: "newline", tok: groups[0], value: groups[0]});

            // comment
            } else if (groups = /^\s*{\*(.*?)\*}/.exec(body)) {
                toks.push({type: "comment", tok: groups[0], value: groups[1]});
            // value, opentag, closetag
            } else if (groups = /^(\$|\s*){(\/?)([^\r\n{}]+)}/.exec(body)) {
                var type = "opentag";
                if (groups[1] === "$") { type = "value";
                } else if (groups[2] === "/") { type = "closetag"; }
                var value = groups[3].replace(/^\s+|\s+$/g, ""); // trim space
                toks.push({type: type, tok: groups[0],
                    value: value, words: value.split(/\s+/)});

            // text
            } else if ((groups = /^[^\r\n${]+/.exec(body)) ||
                    (groups = /^[^\r\n]+/.exec(body))) {
                toks.push({type: "text", tok: groups[0], value: groups[0]});
            } else {
                // impossible
                throw new Error("Could not parse: " + body);
            }
            body = body.substring(groups[0].length);
        }

        // generator variables
        var tagstack = [];  // used by generator to match open/close tags
        var inEval = false; // gen handles tokens differently in eval-mode
        var inText = false; // similar to 'inEval'
        var textparts = []; // text parts are grouped for better render perf
        var fcount = 0;     // number of functions on tagstack
        // src is output of generator
        // First line of 'src' creates template object within 'JST.templates'
        // If template declares prototype, src[0] is replaced with:
        //   JST.get(name, proto);
        var src = ['JST.get("' + name + '"); '];

        // add specified string to src.
        // first put text parts into single 'out.write' statement
        var addsrc = function(s) {
            // first push text onto src
            if (textparts.length) {
                src.push('out.write("' + textparts.join('')
                        .replace(/\r?\n|\r|"/g, function(str) {
                            switch(str) {
                            case "\r\n": return "\\r\\n\\\n";
                            case '"': return '\\"';
                            case "\n": return "\\n\\\n";
                            case "\r": return "\\r\\\n";
                            }
                        }) + '"); ');
                textparts = [];
            }
            src.push(s);
        };

        // errors during generator
        var error = function(b, desc) {
            if (b) {
                throw new Error(desc + ", line: " + line + "."  + linepos
                        + ", tagstack: [" + tagstack.join(" > ") + "]");
            }
        };

        // Generate JS template that can be eval'ed
        // Take care to match line numbers of generated JS with
        // line numbers of source.  We then rely on JS interpreter to
        // provide good error messages

        // skip blank lines at start
        while (toks.length > 0 && toks[0].type === "newline") {
            toks.shift();
            src.push("\n");
        }

        // either set up 'prototype' or wrap toks with 'function render'
        if (toks.length > 0 && (toks[0].type === "opentag"
                    && toks[0].words[0] === "prototype")) {
                var tok = toks.shift();
                error(tok.words.length !== 2,
                        "invalid prototype tag format: " + tok.tok);
                // create empty prototype if it doesn't yet exist
                src[0] = 'JST.get("' + name + '", "' + tok.words[1] + '"); ';
        } else {
            // wrap with render (front and back)
            toks.unshift({type: "opentag", tok: "{function render}",
                value: "function render", words: ["function", "render"]});
            toks.push({type: "closetag", tok: "{/function render}",
                value: "function render", words: ["function", "render"]});
        }

        for (var i = 0; i < toks.length; i++) {
            var tok = toks[i];
            var toptag = tagstack[tagstack.length - 1];
            // newline
            if (tok.type === "newline") {
                // update line, pos is increased at bottom of for-loop
                line++;
                linepos = 1 - tok.value.length;
                // check if newline is formatting only, or needs to be rendered
                // formatting only if we are in {eval}...{/eval},
                // or if line contains only open/close tags, or if fcount === 0
                var j = i;
                while (j > 0 && /^(opentag|closetag|comment)$/.test(toks[--j].type));
                var tags = j < i-1 && (j === 0 || toks[j].type === "newline");
                if (inEval || tags || fcount === 0) {
                    addsrc("\n"); // formatting only
                } else {
                    textparts.push(tok.value); // needs to be rendered
                }

            // are we at end of {text?}...{/text?} or {eval}...{/eval}
            } else if ((inText || inEval) && tok.type === "closetag"
                    && tok.value === toptag) {
                addsrc("; ");
                // we are now finished 'text' or 'eval' section
                inText = inEval = false;
                tagstack.pop();
            } else if (inText) { // still in text
                textparts.push(tok.tok);
            } else if (inEval) { // still in eval
                addsrc(tok.value);

            // comment
            } else if (tok.type === "comment") {
                // do nothing - ignore comment

            // value substitution
            } else if (tok.type === "value") {
                addsrc('h(' + tok.value + ', out); '); // html-escape

            // open tag
            } else if (tok.type === "opentag") {
                if (tok.words[0] === "function") {
                    error(tok.words.length !== 2,
                            "invalid function tag format: " + tok.tok);
                    error(tok.words[1] === "render" && i !== 0,
                            "function 'render' not allowed");
                    // inner functions are wrapped within
                    // 'if (!JST.templates[name].hasOwnProperty(name)) { ...'
                    if (fcount > 0) {
                        addsrc('if (!JST.templates["' + name
                            + '"].hasOwnProperty("' + tok.words[1] + '")) { ');
                    }
                    addsrc('JST.templates["' + name + '"].' + tok.words[1]
                        + ' = function (out, cx) { with (cx) { ');
                    fcount++;
                    tagstack.push(tok.value); // push 'function <fname>'
                } else if (tok.words[0] === "render") {
                    error(tok.words.length !== 2,
                            "invalid render tag format: " + tok.tok);
                    addsrc('JST.templates["' + tok.words[1] + '"].render(out, cx); ');
                } else if (tok.words[0] === "if") {
                    addsrc(tok.value + " {");
                    tagstack.push("if");
                } else if (tok.words[0].match(/^els?e?(if)?$/)) {
                    error(!toptag || !toptag.match(/^(if|els?e?(if)?)$/),
                            "unexpected else(if)? tag");
                    var ifword = tok.words[0] === "else" ? "" : " if ";
                    addsrc("} else " + ifword
                            + tok.words.slice(1).join(" ") + " {");
                } else if (tok.words[0] === "for") {
                    addsrc("var forcounter = 0; " +
                            tok.value + " { forcounter++; ");
                    tagstack.push("for");
                } else if (tok.words[0] === "forelse") {
                    error(toptag !== "for", "unexpected forelse tag");
                    addsrc("} if (forcounter === 0) { ");
                } else if (tok.words[0] === "eval") {
                    if (tok.words.length === 1) {
                         inEval = true;
                         tagstack.push("eval");
                    } else { // inline eval
                        addsrc(tok.words.slice(1).join(" ") + "; ");
                    }
                } else if (tok.words[0].match(/^text/)) {
                    inText = true;
                    tagstack.push(tok.value);
                } else { // treat as text
                    tok.type = "text";
                    textparts.push(tok.tok);
                }

            // close tag
            } else if (tok.type === "closetag") {
                error(toptag !== tok.value, "unexepcted closetag " + tok.tok);
                addsrc("} ");
                tagstack.pop();
                if (tok.words[0] === "function") {
                    addsrc("};");
                    fcount--;
                    // inner functions are wrapped within
                    // 'if (!JST.templates[name].hasOwnProperty(name)) { ...'
                    // and must invoke self
                    if (fcount > 0) {
                        addsrc(" }; this." + tok.words[1] + "(out, cx); ");
                    }
                }

            // text
            } else if (tok.type === "text") {
                // ignore text outside functions
                if (fcount > 0) {
                    textparts.push(tok.value);
                }

            } else {
                // should not happen
                error(true, "unrecognised token type: " + tok.type);
            }
            linepos += tok.value.length
        }

        return src.join("");
    }
}

// JST relies on global method 'h' for html-escape
var h = h || function(s, out) {
    if (!s) return "";
    var result = s.toString().replace(/[&<>"']/g, function(str) {
       switch (str) {
       case "&": return "&";
       case "<": return "<";
       case ">": return ">";
       case '"': return """;
       case "'": return "'";
       default: return str;
       }
    });
    if (out) {
        out.write(result);
    } else {
        return result;
    }
}
<|endoftext|>


<commit_before>/*
 * The "kaltura player" embedPlayer interface for fallback h.264 and flv video format support
 */
( function( mw, $ ) { "use strict";

	mw.EmbedPlayerChromecastReceiver = {
		// Instance name:
		instanceOf : 'ChromecastReceiver',
		bindPostfix: '.ccPlayer',
		// List of supported features:
		supports : {
			'playHead' : true,
			'pause' : true,
			'stop' : true,
			'volumeControl' : true,
			'overlays': true
		},
		seeking: false,
		startOffset: 0,
		currentTime: 0,
		duration: 0,
		userSlide: false,
		volume: 1,
		vid: null,
		monitorInterval: null,
		receiverName: '',
		nativeEvents: [
			'loadstart',
			'progress',
			'suspend',
			'abort',
			'error',
			'emptied',
			'stalled',
			'play',
			'pause',
			'loadedmetadata',
			'loadeddata',
			'waiting',
			'playing',
			'canplay',
			'canplaythrough',
			'seeking',
			'seeked',
			'timeupdate',
			'ended',
			'ratechange',
			'durationchange',
			'volumechange'
		],

		setup: function( readyCallback ) {
			this.vid = this.getPlayerElement();
			this.applyMediaElementBindings();
			mw.log('EmbedPlayerChromecastReceiver:: Setup. Video element: '+this.getPlayerElement().toString());
			$(this).trigger("chromecastReceiverLoaded",[this.getPlayerElement()]);
			var _this = this;
			this._propagateEvents = true;
			$(this.getPlayerElement()).css('position', 'absolute');
			if (this.inline) {
				$(this.getPlayerElement()).attr('webkit-playsinline', '');
			}
			if (this.monitorInterval !== null){
				clearInterval(this.monitorInterval);
			}
			this.monitorInterval = setInterval(function(){_this.monitor();},1000);
			readyCallback();
		},

		/**
		 * Apply media element bindings
		 */
		applyMediaElementBindings: function () {
			var _this = this;
			this.log("MediaElementBindings");
			var vid = this.getPlayerElement();
			if (!vid) {
				this.log(" Error: applyMediaElementBindings without player elemnet");
				return;
			}
			$.each(_this.nativeEvents, function (inx, eventName) {
				if (mw.isIOS8_9() && mw.isIphone() && eventName === "seeking") {
					return;
				}
				$(vid).unbind(eventName + '.embedPlayerChromecastReceiver').bind(eventName + '.embedPlayerChromecastReceiver', function () {
					// make sure we propagating events, and the current instance is in the correct closure.
					if (_this._propagateEvents && _this.instanceOf == 'ChromecastReceiver') {
						var argArray = $.makeArray(arguments);
						// Check if there is local handler:
						if (_this[ '_on' + eventName ]) {
							_this[ '_on' + eventName ].apply(_this, argArray);
						} else {
							// No local handler directly propagate the event to the abstract object:
							$(_this).trigger(eventName, argArray);
						}
					}
				});
			});
		},

		/**
		 * Handle the native paused event
		 */
		_onpause: function () {
			this.paused = true;
			this.layoutBuilder.showPlayerControls();
			$(this).trigger('onPlayerStateChange', [ "pause", "play" ]);
			this.parent_pause();
		},

		/**
		 * Handle the native play event
		 */
		_onplay: function () {
			this.paused = false;
			this.stopped = false;
			this.layoutBuilder.hidePlayerControls();
			$(this).trigger('onPlayerStateChange', [ "play", "pause" ]);
			this.parent_play();
		},
		// override these functions so embedPlayer won't try to sync time
		syncCurrentTime: function(){},

		isInSequence: function(){return false;},

		monitor: function(){
			if ( this.vid && this.vid.currentTime !== null && this.vid.duration !== null) {
				$(this).trigger("updatePlayHeadPercent",[ this.vid.currentTime / this.vid.duration ]);
				$( this ).trigger( 'externalTimeUpdate', [this.vid.currentTime]);
			}
			$(this).trigger( 'monitorEvent' );
		},

		getPlayerElementTime: function(){
			return this.getPlayerElement().currentTime;
		},

		isVideoSiblingEnabled: function() {
			return false;
		}
	};
	} )( mediaWiki, jQuery );
<commit_msg>restore getPlayerElement function<commit_after>/*
 * The "kaltura player" embedPlayer interface for fallback h.264 and flv video format support
 */
( function( mw, $ ) { "use strict";

	mw.EmbedPlayerChromecastReceiver = {
		// Instance name:
		instanceOf : 'ChromecastReceiver',
		bindPostfix: '.ccPlayer',
		// List of supported features:
		supports : {
			'playHead' : true,
			'pause' : true,
			'stop' : true,
			'volumeControl' : true,
			'overlays': true
		},
		seeking: false,
		startOffset: 0,
		currentTime: 0,
		duration: 0,
		userSlide: false,
		volume: 1,
		vid: null,
		monitorInterval: null,
		receiverName: '',
		nativeEvents: [
			'loadstart',
			'progress',
			'suspend',
			'abort',
			'error',
			'emptied',
			'stalled',
			'play',
			'pause',
			'loadedmetadata',
			'loadeddata',
			'waiting',
			'playing',
			'canplay',
			'canplaythrough',
			'seeking',
			'seeked',
			'timeupdate',
			'ended',
			'ratechange',
			'durationchange',
			'volumechange'
		],

		setup: function( readyCallback ) {
			this.vid = this.getPlayerElement();
			this.applyMediaElementBindings();
			mw.log('EmbedPlayerChromecastReceiver:: Setup. Video element: '+this.getPlayerElement().toString());
			$(this).trigger("chromecastReceiverLoaded",[this.getPlayerElement()]);
			var _this = this;
			this._propagateEvents = true;
			$(this.getPlayerElement()).css('position', 'absolute');
			if (this.inline) {
				$(this.getPlayerElement()).attr('webkit-playsinline', '');
			}
			if (this.monitorInterval !== null){
				clearInterval(this.monitorInterval);
			}
			this.monitorInterval = setInterval(function(){_this.monitor();},1000);
			readyCallback();
		},

		/**
		 * Apply media element bindings
		 */
		applyMediaElementBindings: function () {
			var _this = this;
			this.log("MediaElementBindings");
			var vid = this.getPlayerElement();
			if (!vid) {
				this.log(" Error: applyMediaElementBindings without player elemnet");
				return;
			}
			$.each(_this.nativeEvents, function (inx, eventName) {
				if (mw.isIOS8_9() && mw.isIphone() && eventName === "seeking") {
					return;
				}
				$(vid).unbind(eventName + '.embedPlayerChromecastReceiver').bind(eventName + '.embedPlayerChromecastReceiver', function () {
					// make sure we propagating events, and the current instance is in the correct closure.
					if (_this._propagateEvents && _this.instanceOf == 'ChromecastReceiver') {
						var argArray = $.makeArray(arguments);
						// Check if there is local handler:
						if (_this[ '_on' + eventName ]) {
							_this[ '_on' + eventName ].apply(_this, argArray);
						} else {
							// No local handler directly propagate the event to the abstract object:
							$(_this).trigger(eventName, argArray);
						}
					}
				});
			});
		},

		/**
		 * Handle the native paused event
		 */
		_onpause: function () {
			this.paused = true;
			this.layoutBuilder.showPlayerControls();
			$(this).trigger('onPlayerStateChange', [ "pause", "play" ]);
			this.parent_pause();
		},

		/**
		 * Handle the native play event
		 */
		_onplay: function () {
			this.paused = false;
			this.stopped = false;
			this.layoutBuilder.hidePlayerControls();
			$(this).trigger('onPlayerStateChange', [ "play", "pause" ]);
			this.parent_play();
		},
		// override these functions so embedPlayer won't try to sync time
		syncCurrentTime: function(){},

		isInSequence: function(){return false;},

		monitor: function(){
			if ( this.vid && this.vid.currentTime !== null && this.vid.duration !== null) {
				$(this).trigger("updatePlayHeadPercent",[ this.vid.currentTime / this.vid.duration ]);
				$( this ).trigger( 'externalTimeUpdate', [this.vid.currentTime]);
			}
			$(this).trigger( 'monitorEvent' );
		},

		getPlayerElement: function () {
			this.playerElement = $('#' + this.pid).get(0);
			return this.playerElement;
		},

		getPlayerElementTime: function(){
			return this.getPlayerElement().currentTime;
		},

		isVideoSiblingEnabled: function() {
			return false;
		}
	};
	} )( mediaWiki, jQuery );
<|endoftext|>


<commit_before>;(function() {
  "use strict";

  require("BaseController").init({
    siteName: "Jango",
    playPause: "#btn-playpause",
    playNext: "#btn-ff",
    mute: "#player_volume_icon",
    iframe: "[name=content]",
    like: "#player_fav_icon",
    dislike: "#player_ban_icon",

    playState: "#btn-playpause.pause",
    song: "#current-song",
    artist: "#player_current_artist > a"
  });
})();
<commit_msg>Fix Jango.<commit_after>;(function() {
  "use strict";

  require("BaseController").init({
    siteName: "Jango",
    playPause: "#btn-playpause",
    playNext: "#btn-ff",
    like: "#btn-fav",
    dislike: "#player_ban",

    playState: "#btn-playpause.pause",
    song: "#current-song",
    artist: "#player_current_artist > a"
  });
})();
<|endoftext|>


<commit_before>
const bot = require('./../bot');
const nemoji = require('node-emoji');
const request = require('request-promise-native')
/**
 * This method should return the response directly to the channel
 * @param {*string array} params
 * @param {*message} message
 */
async function command(params, message) {
    if (params[0]) {
        if (/\<\:.+\:\d+\>/.test(params[0])) {
            let url = `https://cdn.discordapp.com/emojis/${params[0].match(/(\d+)/)[1]}.png`;
            message.channel.send('', { file: url });
        } else {
            let em = await request({
                uri: 'https://api.github.com/emojis',
                headers: {
                    'User-Agent': 'Revive-Bot'
                }
            });
            em = JSON.parse(em);
            let url = em[params[0]];
            if (url)
                message.channel.send('', { file: url });
            else
                message.channel.sendMessage('Invalid emoji');
        }
    } else
        message.channel.sendMessage('Not enough arguments!');

}
/**
 * description of the command
 */
const description = "Gets url of the emoji";
/**
 * Define Exports
 */
module.exports = {
    execute: command,
    description: description
};
<commit_msg>emoji attachment name fix<commit_after>
const bot = require('./../bot');
const nemoji = require('node-emoji');
const request = require('request-promise-native')
/**
 * This method should return the response directly to the channel
 * @param {*string array} params
 * @param {*message} message
 */
async function command(params, message) {
    if (params[0]) {
        if (/\<\:.+\:\d+\>/.test(params[0])) {
            let url = `https://cdn.discordapp.com/emojis/${params[0].match(/(\d+)/)[1]}.png`;
            message.channel.send('', { file: url });
        } else {
            let em = await request({
                uri: 'https://api.github.com/emojis',
                headers: {
                    'User-Agent': 'Revive-Bot'
                }
            });
            em = JSON.parse(em);
            let url = em[params[0]];
            if (url)
                message.channel.send('', { file: { attachment: url, name: params[0] + ".png" } });
            else
                message.channel.sendMessage('Invalid emoji');
        }
    } else
        message.channel.sendMessage('Not enough arguments!');

}
/**
 * description of the command
 */
const description = "Gets url of the emoji";
/**
 * Define Exports
 */
module.exports = {
    execute: command,
    description: description
};
<|endoftext|>


<commit_before>(function() {
    'use strict';

    const chalk = require('chalk');
    const request = require('request');
    const SensorTag = require('sensortag');
    const express = require('express');
    const app = express();
    const bodyParser = require("body-parser");
    const chance = require('chance').Chance();
    const _ = require('lodash');

    let serverPort = process.env.AGILE_SERVER_PORT || 8000;
    function errorHandler(err, req, res, next) {
      res.status(500);
      res.render('error', { error: err });
    }

    app.use(bodyParser.json());
    app.use(function(req, res, next) {
      res.header("Access-Control-Allow-Origin", "*");
      res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
      res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      next();
    });
    app.use(errorHandler);

    var agileDevices = [] // these are "registered devices"
    var wildDevices = require('./devices.json') // devices found by "/discovery"
    var discovery = {
      on: false,
      protocols: [
        {
          "name": "BLE",
          "status": "RUNNING"
        },
        {
          "name": "ZigBee",
          "status": "NONE"
        },
        {
          "name": "ZWave",
          "status": "FAILURE"
        }
      ]
    }

    app.get('/api/protocols/devices', function(req, res) {
      // only send response if discover mode is turned on.
      if (discovery.on) {
        res.status(200).send(wildDevices)
      } else {
        res.status(200).send([])
      }
    });

    app.get('/api/devices', function(req, res) {
      res.status(200).send(agileDevices)
    });

    app.post('/api/devices', function(req, res) {
      var device = req.body
      agileDevices.push(device)
      _.pullAllWith(wildDevices, [device], _.isEqual);
      res.status(200).send(device)
    });

    app.delete('/api/devices/:deviceid', function(req, res) {
      var deviceid = req.params.deviceid
      device = _.find(agileDevices, { 'id': deviceid });
      wildDevices.push(device)
      _.pullAllWith(agileDevices, [device], _.isEqual);
      res.status(204).send()
    });

    app.get('/api/devices/:deviceid', function(req, res) {
      var deviceid = req.params.deviceid
      device = _.find(agileDevices, { 'id': deviceid });
      res.status(200).send(device)
    });

    app.get('/api/protocols/discovery', function(req, res) {
      res.status(200).send(discovery)
    });

    app.post('/api/protocols/discovery', function(req, res) {
      discovery.on = true
      console.log('AgileServer: discovery started');
      SensorTag.discoverAll(onDiscover);
      res.status(200).send(discovery)
    });

    app.delete('/api/protocols/discovery', function(req, res) {
      discovery.on = false
      SensorTag.stopDiscoverAll(onStopDiscover);
      res.status(204).send()
    });

    app.post('/api/protocols/discovery', function(req, res) {
      discovery.on = true
      res.status(200).send(discovery)
    });

    app.get('/api/protocols/discovery', function(req, res) {
      console.log(discovery.on)
      if (discovery.on)
        res.status(200).send(discovery.protocols)
      else {
        res.status(200).send([])
      }
    });

    app.get('/api/device/:deviceId/:componentId/lastUpdate', function(req, res) {
      var newDate = new Date(Date.now());
      dateString = newDate.toUTCString();

      lastUpdate = {
        id: req.params.componentId,
        value: chance.integer({min: -20, max: 20}),
        time: dateString
      }
      res.status(200).send(lastUpdate)
    });

    app.post('/api/device/:deviceid/:componentid/subscribe', function(req, res) {
      // responds with data stream
      res.status(200)
    });

    app.listen(serverPort, function() {
      console.log(chalk.cyan('Agile Server listening on port '+serverPort));
    });

    function onDiscover(sensorTag) {
      console.log('AgileServer: discovered new device: '+chalk.cyan(sensorTag.type)+' with id: '+chalk.cyan(sensorTag.id));
    }
    function onStopDiscover(sensorTag) {
      console.log('AgileServer: discovery stopped');
    }
})();
<commit_msg>fixed missing var init<commit_after>(function() {
    'use strict';

    const chalk = require('chalk');
    const request = require('request');
    const SensorTag = require('sensortag');
    const express = require('express');
    const app = express();
    const bodyParser = require("body-parser");
    const chance = require('chance').Chance();
    const _ = require('lodash');

    let serverPort = process.env.AGILE_SERVER_PORT || 8000;
    function errorHandler(err, req, res, next) {
      res.status(500);
      res.render('error', { error: err });
    }

    app.use(bodyParser.json());
    app.use(function(req, res, next) {
      res.header("Access-Control-Allow-Origin", "*");
      res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
      res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      next();
    });
    app.use(errorHandler);

    var agileDevices = [] // these are "registered devices"
    var wildDevices = require('./devices.json') // devices found by "/discovery"
    var device;
    var discovery = {
      on: false,
      protocols: [
        {
          "name": "BLE",
          "status": "RUNNING"
        },
        {
          "name": "ZigBee",
          "status": "NONE"
        },
        {
          "name": "ZWave",
          "status": "FAILURE"
        }
      ]
    }

    app.get('/api/protocols/devices', function(req, res) {
      // only send response if discover mode is turned on.
      if (discovery.on) {
        res.status(200).send(wildDevices)
      } else {
        res.status(200).send([])
      }
    });

    app.get('/api/devices', function(req, res) {
      res.status(200).send(agileDevices)
    });

    app.post('/api/devices', function(req, res) {
      var device = req.body
      agileDevices.push(device)
      _.pullAllWith(wildDevices, [device], _.isEqual);
      res.status(200).send(device)
    });

    app.delete('/api/devices/:deviceid', function(req, res) {
      var deviceid = req.params.deviceid
      device = _.find(agileDevices, { 'id': deviceid });
      wildDevices.push(device)
      _.pullAllWith(agileDevices, [device], _.isEqual);
      res.status(204).send()
    });

    app.get('/api/devices/:deviceid', function(req, res) {
      var deviceid = req.params.deviceid
      device = _.find(agileDevices, { 'id': deviceid });
      res.status(200).send(device)
    });

    app.get('/api/protocols/discovery', function(req, res) {
      res.status(200).send(discovery)
    });

    app.post('/api/protocols/discovery', function(req, res) {
      discovery.on = true
      console.log('AgileServer: discovery started');
      SensorTag.discoverAll(onDiscover);
      res.status(200).send(discovery)
    });

    app.delete('/api/protocols/discovery', function(req, res) {
      discovery.on = false
      SensorTag.stopDiscoverAll(onStopDiscover);
      res.status(204).send()
    });

    app.post('/api/protocols/discovery', function(req, res) {
      discovery.on = true
      res.status(200).send(discovery)
    });

    app.get('/api/protocols/discovery', function(req, res) {
      console.log(discovery.on)
      if (discovery.on)
        res.status(200).send(discovery.protocols)
      else {
        res.status(200).send([])
      }
    });

    app.get('/api/device/:deviceId/:componentId/lastUpdate', function(req, res) {
      var newDate = new Date(Date.now());
      dateString = newDate.toUTCString();

      lastUpdate = {
        id: req.params.componentId,
        value: chance.integer({min: -20, max: 20}),
        time: dateString
      }
      res.status(200).send(lastUpdate)
    });

    app.post('/api/device/:deviceid/:componentid/subscribe', function(req, res) {
      // responds with data stream
      res.status(200)
    });

    app.listen(serverPort, function() {
      console.log(chalk.cyan('Agile Server listening on port '+serverPort));
    });

    function onDiscover(sensorTag) {
      console.log('AgileServer: discovered new device: '+chalk.cyan(sensorTag.type)+' with id: '+chalk.cyan(sensorTag.id));
    }
    function onStopDiscover(sensorTag) {
      console.log('AgileServer: discovery stopped');
    }
})();
<|endoftext|>


<commit_before>'use strict';

/* Controllers */

angular.module('raw.controllers', [])

  .controller('RawCtrl', function ($scope, dataService, $http, $timeout) {

    $scope.loading = false;

    // Clipboard
    $scope.$watch('clipboardText', function (text) {
      if (!text) return;

      $scope.loading = true;

      if (is.url(text)) {
        $scope.importMode = 'url';
        $timeout(function() { $scope.url = text; });
        return;
      }

      try {
        var json = JSON.parse(text);
        selectArray(json);
        $scope.loading = false;
      }
      catch(error) {
        parseText(text);
      }

    });

    $scope.antani = function(d){
      $scope.loading = true;
      var json = dataService.flatJSON(d);
      parseText(d3.tsv.format(json))
    }

    // select Array in JSON
    function selectArray(json){
      $scope.json = json;
      $scope.structure = [];
      expand(json);
    }

    // parse Text
    function parseText(text){
    //  $scope.loading = false;
      $scope.json = null;
      $scope.text = text;
      $scope.parse(text);
    }

    // load File
    $scope.uploadFile = function (files) {

      if (files && files.length) {

        $scope.loading = true;

        var file = files[0];

        // excel
        if (file.name.search(/\.xls|\.xlsx/) != -1 || file.type.search('sheet') != -1) {
          dataService.loadExcel(file)
          .then(function(worksheets){
            $scope.fileName = file.name;
            $scope.loading = false;
            // multiple sheets
            if (worksheets.length > 1) {
              $scope.worksheets = worksheets;
            // single > parse
            } else {
              $scope.parse(worksheets[0].text);
            }
          })
        }

        // json
        if (file.type.search('json') != -1) {
          dataService.loadJson(file)
          .then(function(json){
            $scope.fileName = file.name;
            selectArray(json);
          })
        }

        // txt
        if (file.type.search('text') != -1) {
          dataService.loadText(file)
          .then(function(text){
            $scope.fileName = file.name;
            parseText(text);
          })
        }
      }
    };

    function parseData(json){

      $scope.loading = false;
    //  $scope.parsed = true;

      if (!json) return;
      try {
        selectArray(json);
      }
      catch(error) {
        console.log(error)
        parseText(json);
      }

    }

    // load URl
    $scope.$watch('url', function (url) {

      if(!url || !url.length) {
        return;
      }

      if (is.not.url(url)) {
        $scope.error = "Please insert a valid URL";
        return;
      }

      $scope.loading = true;
      var error = null;

      // first trying jsonp
      $http.jsonp(url+'?callback=JSON_CALLBACK')
      .success(function(data, status, headers, config) {
        $scope.fileName = url;
        parseData(data);
      })
      .error(function(data, status, headers, config) {
        console.log(data,status,headers)

        $http.get(url, {responseType:'arraybuffer'})
        .success(function(arraybuffer, status, headers, config) {
          console.log(arraybuffer,status,headers)

          var data = new Uint8Array(arraybuffer);
          var arr = new Array();
          for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
          var bstr = arr.join("");

          try {
            var workbook = XLS.read(bstr, {type:"binary"});
            var worksheets = [];
						var sheet_name_list = workbook.SheetNames;

						sheet_name_list.forEach(function(y) {
						  var worksheet = workbook.Sheets[y];
							worksheets.push({
								name: y,
								text : XLSX.utils.sheet_to_csv(worksheet),
                rows: worksheet['!range'].e.r
							})
						});

            $scope.fileName = url;
            $scope.loading = false;

            // multiple sheets
            if (worksheets.length > 1) {
              $scope.worksheets = worksheets;
            // single > parse
            } else {
              parseText(worksheets[0].text);
            }
          }
          catch(error) {
            $scope.fileName = url;
            try {
              var json = JSON.parse(bstr);
              selectArray(json);
            }
            catch(error) {
              parseText(bstr);
            }
          }

        })
        .error(function(data, status, headers, config) {
          $scope.loading = false;
          $scope.error = "Something wrong with the URL you provided. Please be sure it is the correct address.";
        });

      });

    });

    $scope.samples = [
      { title : 'Biggest cities per continent', type : 'Distributions', url : 'data/cities.csv'},
      { title : 'Countries GDP', type : 'Other', url : 'data/countriesGDP.csv'},
      { title : 'Cars', type : 'Multivariate', url : 'data/multivariate.csv' },
      { title : 'Movies', type : 'Dispersions', url : 'data/dispersions.csv' },
      { title : 'Music industry', type: 'Time Series', url : 'data/music.csv' },
      { title : 'Lineup', type : 'Time chunks', url : 'data/lineup.tsv' },
      { title : 'Orchestras', type : 'Hierarchies (weighted)', url : 'data/orchestra.csv' },
      { title : 'Animal kingdom', type: 'Hierarchies', url : 'data/animals.tsv' },
      { title : 'Titanic\'s passengers', type : 'Multi categorical', url : 'data/titanic.tsv' },
      { title : 'Most frequent letters', type: 'Matrix (narrow)', url:'data/letters.tsv'}
    ]

    $scope.selectSample = function(sample) {

//    $scope.$watch('sample', function (sample){
      if (!sample) return;
      $scope.loading = true;
      dataService.loadSample(sample.url).then(
        function(data){
          $scope.text = data;
          $scope.loading = false;
        },
        function(error){
          $scope.error = error;
          $scope.loading = false;
        }
      );
    }//);

    $(document).on('dragenter', function(e){
      $scope.importMode = 'file';
      $scope.parsed = false;
      $scope.$digest();
    })

    $scope.$watch('importMode', function (n,o){

      $scope.parsed = false;
      $scope.loading = false;
      $scope.clipboardText = "";
      $scope.unstacked = false;
      $scope.text = "";
      $scope.data = [];
      $scope.json = null;
      $scope.worksheets = [];
      $scope.fileName = null;
    });

    $scope.$watch('dataView', function (n,o){
      if (!$('.parsed .CodeMirror')[0]) return;
      var cm = $('.parsed .CodeMirror')[0].CodeMirror;
      $timeout(function() { cm.refresh()});
    });

    // init
    $scope.raw = raw;
    $scope.data = [];
    $scope.metadata = [];
    $scope.error = false;
  //  $scope.loading = true;

    $scope.importMode = 'clipboard';

    $scope.categories = ['Hierarchies', 'Time Series', 'Distributions', 'Correlations', 'Others'];

    $scope.bgColors = {
      'Hierarchies': '#0f0',
      'Time Series': 'rgb(255, 185, 5)',
      'Distributions': 'rgb(5, 205, 255)',
      'Correlations': '#df0',
      'Others': '#0f0'
    }

    $scope.$watch('files', function () {
      $scope.uploadFile($scope.files);
    });

    $scope.log = '';

    $scope.files=[];

    $scope.$watch('importMode', function(){
      // reset
      $scope.clipboardText = "";
      $scope.url = "";
      //$scope.$apply();
    })

    var arrays = [];

    $scope.unstack = function(){
      if (!$scope.stackDimension) return;
      var data = $scope.data;
      var base = $scope.stackDimension.key;

      var unstacked = [];

      data.forEach(function(row){
        for (var column in row) {
            if (column == base) continue;
            var obj = {};
            obj[base] = row[base];
            obj.column = column;
            obj.value = row[column];
            unstacked.push(obj);
          }
      })
      $scope.oldData = data;
      parseText(d3.tsv.format(unstacked));

      $scope.unstacked = true;

    }

    $scope.stack = function(){
      parseText(d3.tsv.format($scope.oldData));
      $scope.unstacked = false;
    }

    function jsonTree(json){
      // mettere try
      var tree = JSON.parse(json);
      $scope.json = tree;
      $scope.structure = [];
      //console.log(JSON.parse(json));
      expand(tree);
    }

    function expand(parent){
      for (var child in parent) {
        if (is.object(parent[child]) || is.array(parent[child])) {
          expand(parent[child]);
          if (is.array(parent[child])) arrays.push(child);
        }
      }
      //console.log(child,parent[child])
    }

    // very improbable function to determine if pivot table or not.
    // pivotable index
    // calculate if values repeat themselves
    // calculate if values usually appear in more columns

    function pivotable(array) {

      var n = array.length;
      var rows = {};

      array.forEach(function(o){
        for (var p in o) {
          if (!rows.hasOwnProperty(p)) rows[p] = {};
          if (!rows[p].hasOwnProperty(o[p])) rows[p][o[p]] = -1;
          rows[p][o[p]]+=1;
        }
      })

      for (var r in rows) {
        for (var p in rows[r]) {
          for (var ra in rows) {
            if (r == ra) break;
        //    if (p == "") break;
            if (rows[ra].hasOwnProperty(p)) rows[r][p]-=2.5;

          }
        }
      }

      var m = d3.values(rows).map(d3.values).map(function(d){ return d3.sum(d)/n; });
      //console.log(d3.mean(m),m)
      $scope.pivot = d3.mean(m);

    }

    $scope.parse = function(text){

      if ($scope.model) $scope.model.clear();

      $scope.text = text;
      $scope.data = [];
      $scope.metadata = [];
      $scope.error = false;
      //$scope.importMode = null;
      //$scope.$apply();

      if (!text) return;

      try {
        var parser = raw.parser();
        $scope.data = parser(text);
        $scope.metadata = parser.metadata(text);
        $scope.error = false;
        pivotable($scope.data);
        $scope.parsed = true;

      } catch(e){
        $scope.data = [];
        $scope.metadata = [];
        $scope.error = e.name == "ParseError" ? +e.message : false;
      }
      if (!$scope.data.length && $scope.model) $scope.model.clear();
      $scope.loading = false;
      var cm = $('.parsed .CodeMirror')[0].CodeMirror;
      $timeout(function() { cm.refresh()} );
    }

    $scope.delayParse = dataService.debounce($scope.parse, 500, false);

    $scope.$watch("text", function (text){
      if (!text) return;
      $scope.loading = true;
      $scope.delayParse(text);
    });

    $scope.charts = raw.charts.values().sort(function (a,b){ return d3.ascending(a.category(),b.category()) || d3.ascending(a.title(),b.title()) })
    $scope.chart = $scope.charts.filter(function(d){return d.title() == 'Scatter Plot'})[0];
    $scope.model = $scope.chart ? $scope.chart.model() : null;

    $scope.$watch('error', function (error){
      if (!$('.parsed .CodeMirror')[0]) return;
      var cm = $('.parsed .CodeMirror')[0].CodeMirror;
      if (!error) {
        cm.removeLineClass($scope.lastError,'wrap','line-error');
        return;
      }
      cm.addLineClass(error, 'wrap', 'line-error');
      cm.scrollIntoView(error);
      $scope.lastError = error;
    })

    $('body').mousedown(function (e,ui){
      if ($(e.target).hasClass("dimension-info-toggle")) return;
      $('.dimensions-wrapper').each(function (e){
        angular.element(this).scope().open = false;
        angular.element(this).scope().$apply();
      })
    })

    $scope.codeMirrorOptions = {
      dragDrop : false,
      lineNumbers : true,
      lineWrapping : true
    }

    $scope.selectChart = function(chart){
      if (chart == $scope.chart) return;
      $scope.model.clear();
      $scope.chart = chart;
      $scope.model = $scope.chart.model();
    }

    function refreshScroll(){
      $('[data-spy="scroll"]').each(function () {
        $(this).scrollspy('refresh');
      });
    }

    $(window).scroll(function(){

      // check for mobile
      if ($(window).width() < 760 || $('#mapping').height() < 300) return;

      var scrollTop = $(window).scrollTop() + 0,
          mappingTop = $('#mapping').offset().top + 10,
          mappingHeight = $('#mapping').height(),
          isBetween = scrollTop > mappingTop + 50 && scrollTop <= mappingTop + mappingHeight - $(".sticky").height() - 20,
          isOver = scrollTop > mappingTop + mappingHeight - $(".sticky").height() - 20,
          mappingWidth = mappingWidth ? mappingWidth : $('.mapping').width();

      if (mappingHeight-$('.dimensions-list').height() > 90) return;
      //console.log(mappingHeight-$('.dimensions-list').height())
      if (isBetween) {
        $(".sticky")
          .css("position","fixed")
          .css("width", mappingWidth+"px")
          .css("top","20px")
      }

     if(isOver) {
        $(".sticky")
          .css("position","fixed")
          .css("width", mappingWidth+"px")
          .css("top", (mappingHeight - $(".sticky").height() + 0 - scrollTop+mappingTop) + "px");
          return;
      }

      if (isBetween) return;

      $(".sticky")
        .css("position","relative")
        .css("top","")
        .css("width", "");

    })

      $scope.sortCategory = function (chart) {
        // sort first by category, then by title
        return [chart.category(),chart.title()];
      };

    $(document).ready(refreshScroll);

  })
<commit_msg>fix: issue #115<commit_after>'use strict';

/* Controllers */

angular.module('raw.controllers', [])

  .controller('RawCtrl', function ($scope, dataService, $http, $timeout) {

    $scope.loading = false;

    // Clipboard
    $scope.$watch('clipboardText', function (text) {
      if (!text) return;

      $scope.loading = true;

      if (is.url(text)) {
        $scope.importMode = 'url';
        $timeout(function() { $scope.url = text; });
        return;
      }

      try {
        var json = JSON.parse(text);
        selectArray(json);
        $scope.loading = false;
      }
      catch(error) {
        parseText(text);
      }

    });

    $scope.antani = function(d){
      $scope.loading = true;
      var json = dataService.flatJSON(d);
      parseText(d3.tsv.format(json))
    }

    // select Array in JSON
    function selectArray(json){
      $scope.json = json;
      $scope.structure = [];
      expand(json);
    }

    // parse Text
    function parseText(text){
    //  $scope.loading = false;
      $scope.json = null;
      $scope.text = text;
      $scope.parse(text);
    }

    // load File
    $scope.uploadFile = function (files) {

      if (files && files.length) {

        $scope.loading = true;

        var file = files[0];

        // excel
        if (file.name.search(/\.xls|\.xlsx/) != -1 || file.type.search('sheet') != -1) {
          dataService.loadExcel(file)
          .then(function(worksheets){
            $scope.fileName = file.name;
            $scope.loading = false;
            // multiple sheets
            if (worksheets.length > 1) {
              $scope.worksheets = worksheets;
            // single > parse
            } else {
              $scope.parse(worksheets[0].text);
            }
          })
        }

        // json
        if (file.type.search('json') != -1) {
          dataService.loadJson(file)
          .then(function(json){
            $scope.fileName = file.name;
            selectArray(json);
          })
        }

        // txt
        if (file.type.search('text') != -1) {
          dataService.loadText(file)
          .then(function(text){
            $scope.fileName = file.name;
            parseText(text);
          })
        }
      }
    };

    function parseData(json){

      $scope.loading = false;
    //  $scope.parsed = true;

      if (!json) return;
      try {
        selectArray(json);
      }
      catch(error) {
        console.log(error)
        parseText(json);
      }

    }

    // load URl
    $scope.$watch('url', function (url) {

      if(!url || !url.length) {
        return;
      }

      if (is.not.url(url)) {
        $scope.error = "Please insert a valid URL";
        return;
      }

      $scope.loading = true;
      var error = null;

      // first trying jsonp
      $http.jsonp(url+'?callback=JSON_CALLBACK')
      .success(function(data, status, headers, config) {
        $scope.fileName = url;
        parseData(data);
      })
      .error(function(data, status, headers, config) {
        console.log(data,status,headers)

        $http.get(url, {responseType:'arraybuffer'})
        .success(function(arraybuffer, status, headers, config) {
          console.log(arraybuffer,status,headers)

          var data = new Uint8Array(arraybuffer);
          var arr = new Array();
          for(var i = 0; i != data.length; ++i) arr[i] = String.fromCharCode(data[i]);
          var bstr = arr.join("");

          try {
            var workbook = XLS.read(bstr, {type:"binary"});
            var worksheets = [];
						var sheet_name_list = workbook.SheetNames;

						sheet_name_list.forEach(function(y) {
						  var worksheet = workbook.Sheets[y];
							worksheets.push({
								name: y,
								text : XLSX.utils.sheet_to_csv(worksheet),
                rows: worksheet['!range'].e.r
							})
						});

            $scope.fileName = url;
            $scope.loading = false;

            // multiple sheets
            if (worksheets.length > 1) {
              $scope.worksheets = worksheets;
            // single > parse
            } else {
              parseText(worksheets[0].text);
            }
          }
          catch(error) {
            $scope.fileName = url;
            try {
              var json = JSON.parse(bstr);
              selectArray(json);
            }
            catch(error) {
              parseText(bstr);
            }
          }

        })
        .error(function(data, status, headers, config) {
          $scope.loading = false;
          $scope.error = "Something wrong with the URL you provided. Please be sure it is the correct address.";
        });

      });

    });

    $scope.samples = [
      { title : 'Biggest cities per continent', type : 'Distributions', url : 'data/cities.csv'},
      { title : 'Countries GDP', type : 'Other', url : 'data/countriesGDP.csv'},
      { title : 'Cars', type : 'Multivariate', url : 'data/multivariate.csv' },
      { title : 'Movies', type : 'Dispersions', url : 'data/dispersions.csv' },
      { title : 'Music industry', type: 'Time Series', url : 'data/music.csv' },
      { title : 'Lineup', type : 'Time chunks', url : 'data/lineup.tsv' },
      { title : 'Orchestras', type : 'Hierarchies (weighted)', url : 'data/orchestra.csv' },
      { title : 'Animal kingdom', type: 'Hierarchies', url : 'data/animals.tsv' },
      { title : 'Titanic\'s passengers', type : 'Multi categorical', url : 'data/titanic.tsv' },
      { title : 'Most frequent letters', type: 'Matrix (narrow)', url:'data/letters.tsv'}
    ]

    $scope.selectSample = function(sample) {

//    $scope.$watch('sample', function (sample){
      if (!sample) return;
      $scope.loading = true;
      dataService.loadSample(sample.url).then(
        function(data){
          $scope.text = data;
          $scope.loading = false;
        },
        function(error){
          $scope.error = error;
          $scope.loading = false;
        }
      );
    }//);

    $(document.getElementById("load-data")).on('dragenter', function(e){
      $scope.importMode = 'file';
      $scope.parsed = false;
      $scope.$digest();
    });

    $scope.$watch('importMode', function (n,o){

      $scope.parsed = false;
      $scope.loading = false;
      $scope.clipboardText = "";
      $scope.unstacked = false;
      $scope.text = "";
      $scope.data = [];
      $scope.json = null;
      $scope.worksheets = [];
      $scope.fileName = null;
    });

    $scope.$watch('dataView', function (n,o){
      if (!$('.parsed .CodeMirror')[0]) return;
      var cm = $('.parsed .CodeMirror')[0].CodeMirror;
      $timeout(function() { cm.refresh()});
    });

    // init
    $scope.raw = raw;
    $scope.data = [];
    $scope.metadata = [];
    $scope.error = false;
  //  $scope.loading = true;

    $scope.importMode = 'clipboard';

    $scope.categories = ['Hierarchies', 'Time Series', 'Distributions', 'Correlations', 'Others'];

    $scope.bgColors = {
      'Hierarchies': '#0f0',
      'Time Series': 'rgb(255, 185, 5)',
      'Distributions': 'rgb(5, 205, 255)',
      'Correlations': '#df0',
      'Others': '#0f0'
    }

    $scope.$watch('files', function () {
      $scope.uploadFile($scope.files);
    });

    $scope.log = '';

    $scope.files=[];

    $scope.$watch('importMode', function(){
      // reset
      $scope.clipboardText = "";
      $scope.url = "";
      //$scope.$apply();
    })

    var arrays = [];

    $scope.unstack = function(){
      if (!$scope.stackDimension) return;
      var data = $scope.data;
      var base = $scope.stackDimension.key;

      var unstacked = [];

      data.forEach(function(row){
        for (var column in row) {
            if (column == base) continue;
            var obj = {};
            obj[base] = row[base];
            obj.column = column;
            obj.value = row[column];
            unstacked.push(obj);
          }
      })
      $scope.oldData = data;
      parseText(d3.tsv.format(unstacked));

      $scope.unstacked = true;

    }

    $scope.stack = function(){
      parseText(d3.tsv.format($scope.oldData));
      $scope.unstacked = false;
    }

    function jsonTree(json){
      // mettere try
      var tree = JSON.parse(json);
      $scope.json = tree;
      $scope.structure = [];
      //console.log(JSON.parse(json));
      expand(tree);
    }

    function expand(parent){
      for (var child in parent) {
        if (is.object(parent[child]) || is.array(parent[child])) {
          expand(parent[child]);
          if (is.array(parent[child])) arrays.push(child);
        }
      }
      //console.log(child,parent[child])
    }

    // very improbable function to determine if pivot table or not.
    // pivotable index
    // calculate if values repeat themselves
    // calculate if values usually appear in more columns

    function pivotable(array) {

      var n = array.length;
      var rows = {};

      array.forEach(function(o){
        for (var p in o) {
          if (!rows.hasOwnProperty(p)) rows[p] = {};
          if (!rows[p].hasOwnProperty(o[p])) rows[p][o[p]] = -1;
          rows[p][o[p]]+=1;
        }
      })

      for (var r in rows) {
        for (var p in rows[r]) {
          for (var ra in rows) {
            if (r == ra) break;
        //    if (p == "") break;
            if (rows[ra].hasOwnProperty(p)) rows[r][p]-=2.5;

          }
        }
      }

      var m = d3.values(rows).map(d3.values).map(function(d){ return d3.sum(d)/n; });
      //console.log(d3.mean(m),m)
      $scope.pivot = d3.mean(m);

    }

    $scope.parse = function(text){

      if ($scope.model) $scope.model.clear();

      $scope.text = text;
      $scope.data = [];
      $scope.metadata = [];
      $scope.error = false;
      //$scope.importMode = null;
      //$scope.$apply();

      if (!text) return;

      try {
        var parser = raw.parser();
        $scope.data = parser(text);
        $scope.metadata = parser.metadata(text);
        $scope.error = false;
        pivotable($scope.data);
        $scope.parsed = true;

      } catch(e){
        $scope.data = [];
        $scope.metadata = [];
        $scope.error = e.name == "ParseError" ? +e.message : false;
      }
      if (!$scope.data.length && $scope.model) $scope.model.clear();
      $scope.loading = false;
      var cm = $('.parsed .CodeMirror')[0].CodeMirror;
      $timeout(function() { cm.refresh()} );
    }

    $scope.delayParse = dataService.debounce($scope.parse, 500, false);

    $scope.$watch("text", function (text){
      if (!text) return;
      $scope.loading = true;
      $scope.delayParse(text);
    });

    $scope.charts = raw.charts.values().sort(function (a,b){ return d3.ascending(a.category(),b.category()) || d3.ascending(a.title(),b.title()) })
    $scope.chart = $scope.charts.filter(function(d){return d.title() == 'Scatter Plot'})[0];
    $scope.model = $scope.chart ? $scope.chart.model() : null;

    $scope.$watch('error', function (error){
      if (!$('.parsed .CodeMirror')[0]) return;
      var cm = $('.parsed .CodeMirror')[0].CodeMirror;
      if (!error) {
        cm.removeLineClass($scope.lastError,'wrap','line-error');
        return;
      }
      cm.addLineClass(error, 'wrap', 'line-error');
      cm.scrollIntoView(error);
      $scope.lastError = error;
    })

    $('body').mousedown(function (e,ui){
      if ($(e.target).hasClass("dimension-info-toggle")) return;
      $('.dimensions-wrapper').each(function (e){
        angular.element(this).scope().open = false;
        angular.element(this).scope().$apply();
      })
    })

    $scope.codeMirrorOptions = {
      dragDrop : false,
      lineNumbers : true,
      lineWrapping : true
    }

    $scope.selectChart = function(chart){
      if (chart == $scope.chart) return;
      $scope.model.clear();
      $scope.chart = chart;
      $scope.model = $scope.chart.model();
    }

    function refreshScroll(){
      $('[data-spy="scroll"]').each(function () {
        $(this).scrollspy('refresh');
      });
    }

    $(window).scroll(function(){

      // check for mobile
      if ($(window).width() < 760 || $('#mapping').height() < 300) return;

      var scrollTop = $(window).scrollTop() + 0,
          mappingTop = $('#mapping').offset().top + 10,
          mappingHeight = $('#mapping').height(),
          isBetween = scrollTop > mappingTop + 50 && scrollTop <= mappingTop + mappingHeight - $(".sticky").height() - 20,
          isOver = scrollTop > mappingTop + mappingHeight - $(".sticky").height() - 20,
          mappingWidth = mappingWidth ? mappingWidth : $('.mapping').width();

      if (mappingHeight-$('.dimensions-list').height() > 90) return;
      //console.log(mappingHeight-$('.dimensions-list').height())
      if (isBetween) {
        $(".sticky")
          .css("position","fixed")
          .css("width", mappingWidth+"px")
          .css("top","20px")
      }

     if(isOver) {
        $(".sticky")
          .css("position","fixed")
          .css("width", mappingWidth+"px")
          .css("top", (mappingHeight - $(".sticky").height() + 0 - scrollTop+mappingTop) + "px");
          return;
      }

      if (isBetween) return;

      $(".sticky")
        .css("position","relative")
        .css("top","")
        .css("width", "");

    })

      $scope.sortCategory = function (chart) {
        // sort first by category, then by title
        return [chart.category(),chart.title()];
      };

    $(document).ready(refreshScroll);

  })
<|endoftext|>


<commit_before>const merge = require('webpack-merge')
const path = require('path');
const getCommonConfig = require('../getCommonConfig');
const utils = require('../utils.js');
const {MvvmGraphPlugin} = require('mvvm-pack');
const resolve = require('resolve');
const originSourceLoader = {
  loader: path.join(__dirname, './originSourceLoader.js')
};

module.exports = function(options) {
  let {type, media} = options;
  let npmName = cml.config.get().extPlatform[type];
  let PlatformPlugin = require(resolve.sync(npmName, { basedir: cml.projectRoot }));
  // 用户端扩展插件
  let platformPlugin = new PlatformPlugin({
    cmlType: type,
    media
  });
  cml.extPlatformPlugin[type] = platformPlugin; // 扩展新端插件， utils中获取内置组件需要用到
  // 扩展新端编译默认配置
  if (platformPlugin.cmlConfig) {
    cml.config.merge({
      [type]: {
        ...platformPlugin.cmlConfig
      }
    });
  }

  function getCmlLoaders() {
    let loaders = utils.cssLoaders({type, media});
    loaders.js = [
      loaders.js,
      originSourceLoader
    ]
    return loaders;
  }
  let extendConfig = {
    entry: {
      app: path.join(cml.projectRoot, 'src/app/app.cml')
    },
    output: {
      path: path.join(cml.projectRoot, 'dist/' + type)
    },
    module: {
      rules: [
        ...utils.styleLoaders({type}),
        {
          test: /\.cml$/,
          use: [{
            loader: 'mvvm-cml-loader',
            options: {
              loaders: getCmlLoaders(),
              cmlType: type,
              media,
              check: cml.config.get().check
            }
          }]
        }
      ]
    },
    plugins: [
      new MvvmGraphPlugin({
        cmlType: type,
        media
      }, platformPlugin)
    ]
  };
  let commonConfig = getCommonConfig(options);
  commonConfig.module.rules.forEach(item => {
    // 静态资源的处理
    if (~['chameleon-url-loader', 'file-loader'].indexOf(item.loader)) {
      item.loader = 'mvvm-file-loader';
      item.options.publicPath = commonConfig.output.publicPath
    }

    if (item.test instanceof RegExp) {
      // interface获取originSource
      if (item.test.test('name.interface')) {
        item.use.splice(1, 0, originSourceLoader)
      }

      // js获取originSource
      if (item.test.test('name.js')) {
        item.use.push(originSourceLoader)
      }
    }
  })

  // 用户可以扩展webpack的rules用于处理特有文件后缀
  if (platformPlugin.webpackRules && platformPlugin.webpackRules instanceof Array) {
    platformPlugin.webpackRules.forEach(rule => {
      if (rule && rule.use && rule.use instanceof Array) {
        rule.use.forEach(item => {
          if (item.needDefaultOptions) {
            item.options = item.options || {};
            item.options = {
              loaders: getCmlLoaders(),
              cmlType: type,
              media,
              ...item.options
            }
            delete item.needDefaultOptions;
          }
        })
      }
    });

    extendConfig = merge(extendConfig, {
      module: {
        rules: platformPlugin.webpackRules
      }
    })
  }

  if (platformPlugin.miniappExt && platformPlugin.miniappExt.rule) {
    extendConfig = merge(extendConfig, {
      module: {
        rules: [
          {
            test: platformPlugin.miniappExt.rule,
            use: [{
              loader: 'mvvm-miniapp-loader',
              options: {
                loaders: utils.cssLoaders({type, media}),
                cmlType: type,
                media,
                mapping: platformPlugin.miniappExt.mapping
              }
            }]
          }
        ]
      }
    })
  }

  return merge(commonConfig, extendConfig);
}<commit_msg>feat: 由于mobx更新至4.8,更新扩展新端中全局process.env.NODE_ENV变量<commit_after>const merge = require('webpack-merge')
const path = require('path');
const getCommonConfig = require('../getCommonConfig');
const utils = require('../utils.js');
const {MvvmGraphPlugin} = require('mvvm-pack');
const resolve = require('resolve');
const originSourceLoader = {
  loader: path.join(__dirname, './originSourceLoader.js')
};

module.exports = function(options) {
  let {type, media} = options;
  let npmName = cml.config.get().extPlatform[type];
  let PlatformPlugin = require(resolve.sync(npmName, { basedir: cml.projectRoot }));
  // 用户端扩展插件
  let platformPlugin = new PlatformPlugin({
    cmlType: type,
    media
  });
  cml.extPlatformPlugin[type] = platformPlugin; // 扩展新端插件， utils中获取内置组件需要用到
  // 扩展新端编译默认配置
  if (platformPlugin.cmlConfig) {
    cml.config.merge({
      [type]: {
        ...platformPlugin.cmlConfig
      }
    });
  }

  function getCmlLoaders() {
    let loaders = utils.cssLoaders({type, media});
    loaders.js = [
      loaders.js,
      originSourceLoader
    ]
    return loaders;
  }
  let extendConfig = {
    entry: {
      app: path.join(cml.projectRoot, 'src/app/app.cml')
    },
    output: {
      path: path.join(cml.projectRoot, 'dist/' + type)
    },
    module: {
      rules: [
        ...utils.styleLoaders({type}),
        {
          test: /\.cml$/,
          use: [{
            loader: 'mvvm-cml-loader',
            options: {
              loaders: getCmlLoaders(),
              cmlType: type,
              media,
              check: cml.config.get().check
            }
          }]
        }
      ]
    },
    plugins: [
      new MvvmGraphPlugin({
        cmlType: type,
        media
      }, platformPlugin)
    ]
  };
  if(media === 'dev'){ //扩展新端dev模式下注入全局变量 'process.env.NODE_ENV': JSON.stringify('development')
    extendConfig.plugins.push(new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development')
    }))
  }else if(media === 'build'){
    extendConfig.plugins.push(new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    }))
  }
  let commonConfig = getCommonConfig(options);
  commonConfig.module.rules.forEach(item => {
    // 静态资源的处理
    if (~['chameleon-url-loader', 'file-loader'].indexOf(item.loader)) {
      item.loader = 'mvvm-file-loader';
      item.options.publicPath = commonConfig.output.publicPath
    }

    if (item.test instanceof RegExp) {
      // interface获取originSource
      if (item.test.test('name.interface')) {
        item.use.splice(1, 0, originSourceLoader)
      }

      // js获取originSource
      if (item.test.test('name.js')) {
        item.use.push(originSourceLoader)
      }
    }
  })

  // 用户可以扩展webpack的rules用于处理特有文件后缀
  if (platformPlugin.webpackRules && platformPlugin.webpackRules instanceof Array) {
    platformPlugin.webpackRules.forEach(rule => {
      if (rule && rule.use && rule.use instanceof Array) {
        rule.use.forEach(item => {
          if (item.needDefaultOptions) {
            item.options = item.options || {};
            item.options = {
              loaders: getCmlLoaders(),
              cmlType: type,
              media,
              ...item.options
            }
            delete item.needDefaultOptions;
          }
        })
      }
    });

    extendConfig = merge(extendConfig, {
      module: {
        rules: platformPlugin.webpackRules
      }
    })
  }

  if (platformPlugin.miniappExt && platformPlugin.miniappExt.rule) {
    extendConfig = merge(extendConfig, {
      module: {
        rules: [
          {
            test: platformPlugin.miniappExt.rule,
            use: [{
              loader: 'mvvm-miniapp-loader',
              options: {
                loaders: utils.cssLoaders({type, media}),
                cmlType: type,
                media,
                mapping: platformPlugin.miniappExt.mapping
              }
            }]
          }
        ]
      }
    })
  }

  return merge(commonConfig, extendConfig);
}<|endoftext|>


<commit_before>import { should } from "chai";

import { Tile } from "../src";

should();

/** @test {Tile} */
describe("Tile", () => {
	/** @test {Tile#constructor} */
	describe("#constructor", () => {
		it("creates from objects", () => {
			let tile = new Tile({
				tileId: 56,
				tilesetId: 1
			});
			tile.tileId.should.equal(56);
			tile.tilesetId.should.equal(1);
		});
		it("creates from strings", () => {
			let tile = new Tile("224:5");
			tile.tileId.should.equal(224);
			tile.tilesetId.should.equal(5);
		});
		it("has empty default values", () => {
			const tile = new Tile();
			tile.tilesetId.should.equal(-1);
			tile.tilesetId.should.equal(-1);
		});
	});

	/** @test {Tile#setTile} */
	describe("#setTile", () => {
		let tile;
		beforeEach(() => {
			tile = new Tile("10:8");
		});

		it("can replace only tile ID", () => {
			tile.setData(15);
			tile.tileId.should.equal(15);
			tile.tilesetId.should.equal(8);
		});
		it("can replace both tile ID and tileset ID", () => {
			tile.setData(84, 2);
			tile.tileId.should.equal(84);
			tile.tilesetId.should.equal(2);
		});
		it("doesn't replace if ID is -1", () => {
			tile.setData(-1, -1);
			tile.tileId.should.equal(10);
			tile.tilesetId.should.equal(8);
		});
		it("does replace empty if told so", () => {
			tile.setData(-1, -1, true);
			tile.tileId.should.equal(-1);
			tile.tilesetId.should.equal(-1);
		});
	});

	/** @test {Tile#toJSON} */
	describe("#toJSON", () => {
		it("stringifies with properties", () => {
			let tile = new Tile({
				tileId: 14,
				tilesetId: 3,
				properties: [
					["property", "is a string"]
				]
			});
			JSON.stringify(tile).should.contain("[\"property\",\"is a string\"]");
		});
		it("stringifies without properties", () => {
			let tile = new Tile("456:3");
			JSON.stringify(tile).should.equal("\"456:3\"");
		});
	});

	/** @test {Tile#clone} */
	describe("#clone", () => {
		let tile;
		beforeEach(() => {
			tile = new Tile({
				tileId: 14,
				tilesetId: 3,
				properties: [
					["property", "is a string"]
				]
			});
		});

		it("clones all the properties of a tile", () => {
			tile.clone().should.deep.equal(tile);
		});
		it("doesn't become the same tile", () => {
			tile.clone().should.not.equal(tile);
		});
	});
});
<commit_msg>Fix test name for setData on Tile<commit_after>import { should } from "chai";

import { Tile } from "../src";

should();

/** @test {Tile} */
describe("Tile", () => {
	/** @test {Tile#constructor} */
	describe("#constructor", () => {
		it("creates from objects", () => {
			let tile = new Tile({
				tileId: 56,
				tilesetId: 1
			});
			tile.tileId.should.equal(56);
			tile.tilesetId.should.equal(1);
		});
		it("creates from strings", () => {
			let tile = new Tile("224:5");
			tile.tileId.should.equal(224);
			tile.tilesetId.should.equal(5);
		});
		it("has empty default values", () => {
			const tile = new Tile();
			tile.tilesetId.should.equal(-1);
			tile.tilesetId.should.equal(-1);
		});
	});

	/** @test {Tile#setData} */
	describe("#setData", () => {
		let tile;
		beforeEach(() => {
			tile = new Tile("10:8");
		});

		it("can replace only tile ID", () => {
			tile.setData(15);
			tile.tileId.should.equal(15);
			tile.tilesetId.should.equal(8);
		});
		it("can replace both tile ID and tileset ID", () => {
			tile.setData(84, 2);
			tile.tileId.should.equal(84);
			tile.tilesetId.should.equal(2);
		});
		it("doesn't replace if ID is -1", () => {
			tile.setData(-1, -1);
			tile.tileId.should.equal(10);
			tile.tilesetId.should.equal(8);
		});
		it("does replace empty if told so", () => {
			tile.setData(-1, -1, true);
			tile.tileId.should.equal(-1);
			tile.tilesetId.should.equal(-1);
		});
	});

	/** @test {Tile#toJSON} */
	describe("#toJSON", () => {
		it("stringifies with properties", () => {
			let tile = new Tile({
				tileId: 14,
				tilesetId: 3,
				properties: [
					["property", "is a string"]
				]
			});
			JSON.stringify(tile).should.contain("[\"property\",\"is a string\"]");
		});
		it("stringifies without properties", () => {
			let tile = new Tile("456:3");
			JSON.stringify(tile).should.equal("\"456:3\"");
		});
	});

	/** @test {Tile#clone} */
	describe("#clone", () => {
		let tile;
		beforeEach(() => {
			tile = new Tile({
				tileId: 14,
				tilesetId: 3,
				properties: [
					["property", "is a string"]
				]
			});
		});

		it("clones all the properties of a tile", () => {
			tile.clone().should.deep.equal(tile);
		});
		it("doesn't become the same tile", () => {
			tile.clone().should.not.equal(tile);
		});
	});
});
<|endoftext|>


<commit_before>import {BalancingTileProvider} from '../tileProvider/balancingTileProvider'
import {MAX_ZOOM, MIN_ZOOM} from '../maps'
import guid from 'guid'

// implements google.maps.MapType
export class GoogleMapsOverlay {
    constructor(tileProvider, {
        google,
        name,
        minZoom = MIN_ZOOM,
        maxZoom = MAX_ZOOM,
    } = {}, busy$) {
        this.tileProvider = new BalancingTileProvider({tileProvider, retries: 3, busy$})
        this.name = name
        this.minZoom = minZoom
        this.maxZoom = maxZoom
        this.tileSize = new google.maps.Size(
            tileProvider.tileSize || 256,
            tileProvider.tileSize || 256
        )
        this.tileElementById = {}
        this.opacity = 1
    }

    getTile({x, y}, zoom, doc) {
        const request = this._toTileRequest({x, y, zoom, minZoom: this.minZoom, doc})
        const element = request.element
        this.tileElementById[element.id] = element
        if (request.outOfBounds) {
            return element
        }

        const tile$ = this.tileProvider.loadTile$(request)
        tile$.subscribe(blob => this.tileProvider.renderTile({doc, element, blob}))
        return element
    }

    releaseTile(tileElement) {
        delete this.tileElementById[tileElement.id]
        this.tileProvider.releaseTile(tileElement)
    }

    setOpacity(opacity) {
        if (this.opacity !== opacity) {
            Object.values(this.tileElementById)
                .forEach(tileElement => tileElement.style.opacity = opacity)
            this.tileProvider.hide(!opacity)
            this.opacity = opacity
        }
    }

    close() {
        this.tileProvider.close()
    }

    _toTileRequest({x, y, zoom, minZoom, doc}) {
        const maxCoord = 1 << zoom
        x = x % maxCoord
        if (x < 0) {
            x += maxCoord
        }
        const id = [this.tileProvider.id, zoom, x, y, guid()].join('/')
        const element = this.tileProvider.createElement(id, doc)
        element.style.opacity = this.opacity
        const outOfBounds = zoom < minZoom || y < 0 || y >= maxCoord
        return {id, x, y, zoom, element, outOfBounds}
    }
}
<commit_msg>Set minZoom to 0 for GoogleMapsOverlays.<commit_after>import {BalancingTileProvider} from '../tileProvider/balancingTileProvider'
import {MAX_ZOOM} from '../maps'
import guid from 'guid'

// implements google.maps.MapType
export class GoogleMapsOverlay {
    constructor(tileProvider, {
        google,
        name,
        minZoom = 0,
        maxZoom = MAX_ZOOM,
    } = {}, busy$) {
        this.tileProvider = new BalancingTileProvider({tileProvider, retries: 3, busy$})
        this.name = name
        this.minZoom = minZoom
        this.maxZoom = maxZoom
        this.tileSize = new google.maps.Size(
            tileProvider.tileSize || 256,
            tileProvider.tileSize || 256
        )
        this.tileElementById = {}
        this.opacity = 1
    }

    getTile({x, y}, zoom, doc) {
        const request = this._toTileRequest({x, y, zoom, minZoom: this.minZoom, doc})
        const element = request.element
        this.tileElementById[element.id] = element
        if (request.outOfBounds) {
            return element
        }

        const tile$ = this.tileProvider.loadTile$(request)
        tile$.subscribe(blob => this.tileProvider.renderTile({doc, element, blob}))
        return element
    }

    releaseTile(tileElement) {
        delete this.tileElementById[tileElement.id]
        this.tileProvider.releaseTile(tileElement)
    }

    setOpacity(opacity) {
        if (this.opacity !== opacity) {
            Object.values(this.tileElementById)
                .forEach(tileElement => tileElement.style.opacity = opacity)
            this.tileProvider.hide(!opacity)
            this.opacity = opacity
        }
    }

    close() {
        this.tileProvider.close()
    }

    _toTileRequest({x, y, zoom, minZoom, doc}) {
        const maxCoord = 1 << zoom
        x = x % maxCoord
        if (x < 0) {
            x += maxCoord
        }
        const id = [this.tileProvider.id, zoom, x, y, guid()].join('/')
        const element = this.tileProvider.createElement(id, doc)
        element.style.opacity = this.opacity
        const outOfBounds = zoom < minZoom || y < 0 || y >= maxCoord
        return {id, x, y, zoom, element, outOfBounds}
    }
}
<|endoftext|>