<commit_before>/**
 * @fileoverview Tests for options.
 * @author George Zahariev
 * See LICENSE in root directory for full license.
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var assert = require("chai").assert,
    options = require("../../lib/options");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

/*
 * This is testing the interface of the options object.
 */

describe("options", function() {
    describe("--help", function() {
        it("should return true for .help when passed", function() {
            var currentOptions = options.parse("--help");
            assert.isTrue(currentOptions.help);
        });
    });

    describe("-h", function() {
        it("should return true for .help when passed", function() {
            var currentOptions = options.parse("-h");
            assert.isTrue(currentOptions.help);
        });
    });

    describe("--config", function() {
        it("should return a string for .config when passed a string", function() {
            var currentOptions = options.parse("--config file");
            assert.isString(currentOptions.config);
            assert.equal(currentOptions.config, "file");
        });
    });

    describe("-c", function() {
        it("should return a string for .config when passed a string", function() {
            var currentOptions = options.parse("-c file");
            assert.isString(currentOptions.config);
            assert.equal(currentOptions.config, "file");
        });
    });

    describe("--ext", function() {
        it("should return an array with one item when passed .jsx", function() {
            var currentOptions = options.parse("--ext .jsx");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".jsx");
        });

        it("should return an array with two items when passed .js and .jsx", function() {
            var currentOptions = options.parse("--ext .jsx --ext .js");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".jsx");
            assert.equal(currentOptions.ext[1], ".js");
        });

        it("should return an array with two items when passed .jsx,.js", function() {
            var currentOptions = options.parse("--ext .jsx,.js");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".jsx");
            assert.equal(currentOptions.ext[1], ".js");
        });

        it("should return an array one item when not passed", function() {
            var currentOptions = options.parse("");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".js");
        });
    });

    describe("--rulesdir", function() {
        it("should return a string for .rulesdir when passed a string", function() {
            var currentOptions = options.parse("--rulesdir /morerules");
            assert.isArray(currentOptions.rulesdir);
            assert.equal(currentOptions.rulesdir, "/morerules");
        });
    });

    describe("--format", function() {
        it("should return a string for .format when passed a string", function() {
            var currentOptions = options.parse("--format compact");
            assert.isString(currentOptions.format);
            assert.equal(currentOptions.format, "compact");
        });

        it("should return stylish for .format when not passed", function() {
            var currentOptions = options.parse("");
            assert.isString(currentOptions.format);
            assert.equal(currentOptions.format, "stylish");
        });
    });

    describe("-f", function() {
        it("should return a string for .format when passed a string", function() {
            var currentOptions = options.parse("-f compact");
            assert.isString(currentOptions.format);
            assert.equal(currentOptions.format, "compact");
        });
    });

    describe("--version", function() {
        it("should return true for .version when passed", function() {
            var currentOptions = options.parse("--version");
            assert.isTrue(currentOptions.version);
        });
    });

    describe("-v", function() {
        it("should return true for .version when passed", function() {
            var currentOptions = options.parse("-v");
            assert.isTrue(currentOptions.version);
        });
    });

    describe("when asking for help", function() {
        it("should return string of help text when called", function() {
            var helpText = options.generateHelp();
            assert.isString(helpText);
        });
    });

    describe("--no-ignore", function() {
        it("should return false for .ignore when passed", function() {
            var currentOptions = options.parse("--no-ignore");
            assert.isFalse(currentOptions.ignore);
        });
    });

    describe("--ignore-path", function() {
        it("should return a string for .ignorePath when passed", function() {
            var currentOptions = options.parse("--ignore-path .gitignore");
            assert.equal(currentOptions.ignorePath, ".gitignore");
        });
    });

    describe("--color", function() {
        it("should return true for .color when passed", function() {
            var currentOptions = options.parse("--color");
            assert.isTrue(currentOptions.color);
        });
    });

    describe("--stdin", function() {
        it("should return true for .stdin when passed", function() {
            var currentOptions = options.parse("--stdin");
            assert.isTrue(currentOptions.stdin);
        });
    });

    describe("--stdin-filename", function() {
        it("should return a string for .stdinFilename when passed", function() {
            var currentOptions = options.parse("--stdin-filename test.js");
            assert.equal(currentOptions.stdinFilename, "test.js");
        });
    });

    describe("--global", function() {
        it("should return an array for a single occurrence", function() {
            var currentOptions = options.parse("--global foo");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 1);
            assert.equal(currentOptions.global[0], "foo");
        });

        it("should split variable names using commas", function() {
            var currentOptions = options.parse("--global foo,bar");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 2);
            assert.equal(currentOptions.global[0], "foo");
            assert.equal(currentOptions.global[1], "bar");
        });

        it("should not split on colons", function() {
            var currentOptions = options.parse("--global foo:false,bar:true");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 2);
            assert.equal(currentOptions.global[0], "foo:false");
            assert.equal(currentOptions.global[1], "bar:true");
        });

        it("should concatenate successive occurrences", function() {
            var currentOptions = options.parse("--global foo:true --global bar:false");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 2);
            assert.equal(currentOptions.global[0], "foo:true");
            assert.equal(currentOptions.global[1], "bar:false");
        });
    });

    describe("--plugin", function() {
        it("should return an array when passed a single occurrence", function() {
            var currentOptions = options.parse("--plugin single");
            assert.isArray(currentOptions.plugin);
            assert.equal(currentOptions.plugin.length, 1);
            assert.equal(currentOptions.plugin[0], "single");
        });

        it("should return an array when passed a comma-delimiated string", function() {
            var currentOptions = options.parse("--plugin foo,bar");
            assert.isArray(currentOptions.plugin);
            assert.equal(currentOptions.plugin.length, 2);
            assert.equal(currentOptions.plugin[0], "foo");
            assert.equal(currentOptions.plugin[1], "bar");
        });

        it("should return an array when passed multiple times", function() {
            var currentOptions = options.parse("--plugin foo --plugin bar");
            assert.isArray(currentOptions.plugin);
            assert.equal(currentOptions.plugin.length, 2);
            assert.equal(currentOptions.plugin[0], "foo");
            assert.equal(currentOptions.plugin[1], "bar");
        });
    });

    describe("--quiet", function() {
        it("should return true for .quiet when passed", function() {
            var currentOptions = options.parse("--quiet");
            assert.isTrue(currentOptions.quiet);
        });
    });

    describe("--max-warnings", function() {
        it("should return correct value for .maxWarnings when passed", function() {
            var currentOptions = options.parse("--max-warnings 10");
            assert.equal(currentOptions.maxWarnings, 10);
        });

        it("should return -1 for .maxWarnings when not passed", function() {
            var currentOptions = options.parse("");
            assert.equal(currentOptions.maxWarnings, -1);
        });
    });

    describe("--init", function() {
        it("should return true for --init when passed", function() {
            var currentOptions = options.parse("--init");
            assert.isTrue(currentOptions.init);
        });
    });

    describe("--fix", function() {
        it("should return true for --fix when passed", function() {
            var currentOptions = options.parse("--fix");
            assert.isTrue(currentOptions.fix);
        });
    });

    describe("--debug", function() {
        it("should return true for --debug when passed", function() {
            var currentOptions = options.parse("--debug");
            assert.isTrue(currentOptions.debug);
        });
    });

    describe("--inline-config", function() {
        it("should return false when passed --no-inline-config", function() {
            var currentOptions = options.parse("--no-inline-config");
            assert.isFalse(currentOptions.inlineConfig);
        });

        it("should return true for --inline-config when empty", function() {
            var currentOptions = options.parse("");
            assert.isTrue(currentOptions.inlineConfig);
        });
    });

    describe("--parser", function() {
        it("should return a string for --parser when passed", function() {
            var currentOptions = options.parse("--parser test");
            assert.equal(currentOptions.parser, "test");
        });

        it("should return a espree if --parser is not passed", function() {
            var currentOptions = options.parse("");
            assert.equal(currentOptions.parser, "espree");
        });
    });
});
<commit_msg>Fix: Adding options unit tests for --ignore-pattern (refs #4507)<commit_after>/**
 * @fileoverview Tests for options.
 * @author George Zahariev
 * See LICENSE in root directory for full license.
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var assert = require("chai").assert,
    options = require("../../lib/options");

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

/*
 * This is testing the interface of the options object.
 */

describe("options", function() {
    describe("--help", function() {
        it("should return true for .help when passed", function() {
            var currentOptions = options.parse("--help");
            assert.isTrue(currentOptions.help);
        });
    });

    describe("-h", function() {
        it("should return true for .help when passed", function() {
            var currentOptions = options.parse("-h");
            assert.isTrue(currentOptions.help);
        });
    });

    describe("--config", function() {
        it("should return a string for .config when passed a string", function() {
            var currentOptions = options.parse("--config file");
            assert.isString(currentOptions.config);
            assert.equal(currentOptions.config, "file");
        });
    });

    describe("-c", function() {
        it("should return a string for .config when passed a string", function() {
            var currentOptions = options.parse("-c file");
            assert.isString(currentOptions.config);
            assert.equal(currentOptions.config, "file");
        });
    });

    describe("--ext", function() {
        it("should return an array with one item when passed .jsx", function() {
            var currentOptions = options.parse("--ext .jsx");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".jsx");
        });

        it("should return an array with two items when passed .js and .jsx", function() {
            var currentOptions = options.parse("--ext .jsx --ext .js");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".jsx");
            assert.equal(currentOptions.ext[1], ".js");
        });

        it("should return an array with two items when passed .jsx,.js", function() {
            var currentOptions = options.parse("--ext .jsx,.js");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".jsx");
            assert.equal(currentOptions.ext[1], ".js");
        });

        it("should return an array one item when not passed", function() {
            var currentOptions = options.parse("");
            assert.isArray(currentOptions.ext);
            assert.equal(currentOptions.ext[0], ".js");
        });
    });

    describe("--rulesdir", function() {
        it("should return a string for .rulesdir when passed a string", function() {
            var currentOptions = options.parse("--rulesdir /morerules");
            assert.isArray(currentOptions.rulesdir);
            assert.equal(currentOptions.rulesdir, "/morerules");
        });
    });

    describe("--format", function() {
        it("should return a string for .format when passed a string", function() {
            var currentOptions = options.parse("--format compact");
            assert.isString(currentOptions.format);
            assert.equal(currentOptions.format, "compact");
        });

        it("should return stylish for .format when not passed", function() {
            var currentOptions = options.parse("");
            assert.isString(currentOptions.format);
            assert.equal(currentOptions.format, "stylish");
        });
    });

    describe("-f", function() {
        it("should return a string for .format when passed a string", function() {
            var currentOptions = options.parse("-f compact");
            assert.isString(currentOptions.format);
            assert.equal(currentOptions.format, "compact");
        });
    });

    describe("--version", function() {
        it("should return true for .version when passed", function() {
            var currentOptions = options.parse("--version");
            assert.isTrue(currentOptions.version);
        });
    });

    describe("-v", function() {
        it("should return true for .version when passed", function() {
            var currentOptions = options.parse("-v");
            assert.isTrue(currentOptions.version);
        });
    });

    describe("when asking for help", function() {
        it("should return string of help text when called", function() {
            var helpText = options.generateHelp();
            assert.isString(helpText);
        });
    });

    describe("--no-ignore", function() {
        it("should return false for .ignore when passed", function() {
            var currentOptions = options.parse("--no-ignore");
            assert.isFalse(currentOptions.ignore);
        });
    });

    describe("--ignore-path", function() {
        it("should return a string for .ignorePath when passed", function() {
            var currentOptions = options.parse("--ignore-path .gitignore");
            assert.equal(currentOptions.ignorePath, ".gitignore");
        });
    });

    describe("--ignore-pattern", function() {
        it("should return a string array for .ignorePattern when passed", function() {
            var currentOptions = options.parse("--ignore-pattern *.js");
            assert.ok(currentOptions.ignorePattern);
            assert.equal(currentOptions.ignorePattern.length, 1);
            assert.equal(currentOptions.ignorePattern[0], "*.js");
        });

        it("should return a string array for multiple values", function() {
            var currentOptions = options.parse("--ignore-pattern *.js --ignore-pattern *.ts");
            assert.ok(currentOptions.ignorePattern);
            assert.equal(currentOptions.ignorePattern.length, 2);
            assert.equal(currentOptions.ignorePattern[0], "*.js");
            assert.equal(currentOptions.ignorePattern[1], "*.ts");
        });
    });

    describe("--color", function() {
        it("should return true for .color when passed", function() {
            var currentOptions = options.parse("--color");
            assert.isTrue(currentOptions.color);
        });
    });

    describe("--stdin", function() {
        it("should return true for .stdin when passed", function() {
            var currentOptions = options.parse("--stdin");
            assert.isTrue(currentOptions.stdin);
        });
    });

    describe("--stdin-filename", function() {
        it("should return a string for .stdinFilename when passed", function() {
            var currentOptions = options.parse("--stdin-filename test.js");
            assert.equal(currentOptions.stdinFilename, "test.js");
        });
    });

    describe("--global", function() {
        it("should return an array for a single occurrence", function() {
            var currentOptions = options.parse("--global foo");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 1);
            assert.equal(currentOptions.global[0], "foo");
        });

        it("should split variable names using commas", function() {
            var currentOptions = options.parse("--global foo,bar");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 2);
            assert.equal(currentOptions.global[0], "foo");
            assert.equal(currentOptions.global[1], "bar");
        });

        it("should not split on colons", function() {
            var currentOptions = options.parse("--global foo:false,bar:true");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 2);
            assert.equal(currentOptions.global[0], "foo:false");
            assert.equal(currentOptions.global[1], "bar:true");
        });

        it("should concatenate successive occurrences", function() {
            var currentOptions = options.parse("--global foo:true --global bar:false");
            assert.isArray(currentOptions.global);
            assert.equal(currentOptions.global.length, 2);
            assert.equal(currentOptions.global[0], "foo:true");
            assert.equal(currentOptions.global[1], "bar:false");
        });
    });

    describe("--plugin", function() {
        it("should return an array when passed a single occurrence", function() {
            var currentOptions = options.parse("--plugin single");
            assert.isArray(currentOptions.plugin);
            assert.equal(currentOptions.plugin.length, 1);
            assert.equal(currentOptions.plugin[0], "single");
        });

        it("should return an array when passed a comma-delimiated string", function() {
            var currentOptions = options.parse("--plugin foo,bar");
            assert.isArray(currentOptions.plugin);
            assert.equal(currentOptions.plugin.length, 2);
            assert.equal(currentOptions.plugin[0], "foo");
            assert.equal(currentOptions.plugin[1], "bar");
        });

        it("should return an array when passed multiple times", function() {
            var currentOptions = options.parse("--plugin foo --plugin bar");
            assert.isArray(currentOptions.plugin);
            assert.equal(currentOptions.plugin.length, 2);
            assert.equal(currentOptions.plugin[0], "foo");
            assert.equal(currentOptions.plugin[1], "bar");
        });
    });

    describe("--quiet", function() {
        it("should return true for .quiet when passed", function() {
            var currentOptions = options.parse("--quiet");
            assert.isTrue(currentOptions.quiet);
        });
    });

    describe("--max-warnings", function() {
        it("should return correct value for .maxWarnings when passed", function() {
            var currentOptions = options.parse("--max-warnings 10");
            assert.equal(currentOptions.maxWarnings, 10);
        });

        it("should return -1 for .maxWarnings when not passed", function() {
            var currentOptions = options.parse("");
            assert.equal(currentOptions.maxWarnings, -1);
        });
    });

    describe("--init", function() {
        it("should return true for --init when passed", function() {
            var currentOptions = options.parse("--init");
            assert.isTrue(currentOptions.init);
        });
    });

    describe("--fix", function() {
        it("should return true for --fix when passed", function() {
            var currentOptions = options.parse("--fix");
            assert.isTrue(currentOptions.fix);
        });
    });

    describe("--debug", function() {
        it("should return true for --debug when passed", function() {
            var currentOptions = options.parse("--debug");
            assert.isTrue(currentOptions.debug);
        });
    });

    describe("--inline-config", function() {
        it("should return false when passed --no-inline-config", function() {
            var currentOptions = options.parse("--no-inline-config");
            assert.isFalse(currentOptions.inlineConfig);
        });

        it("should return true for --inline-config when empty", function() {
            var currentOptions = options.parse("");
            assert.isTrue(currentOptions.inlineConfig);
        });
    });

    describe("--parser", function() {
        it("should return a string for --parser when passed", function() {
            var currentOptions = options.parse("--parser test");
            assert.equal(currentOptions.parser, "test");
        });

        it("should return a espree if --parser is not passed", function() {
            var currentOptions = options.parse("");
            assert.equal(currentOptions.parser, "espree");
        });
    });
});
<|endoftext|>


<commit_before>//
// HTML-based manipulation
//
import { stringTrim } from '../string.js';
import { makeArray } from '../array.js';
import { emptyDomNode } from './manipulation.js';
import { jQueryInstance } from '../jquery.js';

var none = [0, "", ""],
    table = [1, "
", "
"],
    tbody = [2, "

", "

"],
    colgroup = [ 2, "

<colgroup>", "</colgroup>
"],
    tr = [3, "

", "

"],
    select = [1, "<select multiple='multiple'>", "</select>"],
    fieldset = [1, "<fieldset>", "</fieldset>"],
    map = [1, "<map>", "</map>"],
    object = [1, "<object>", "</object>"],
    lookup = {
        'area': map,
        'col': colgroup,
        'colgroup': table,
        'caption': table,
        'legend': fieldset,
        'thead': table,
        'tbody': table,
        'tfoot': table,
        'tr': tbody,
        'td': tr,
        'th': tr,
        'option': select,
        'optgroup': select,
        'param': object
    },

    // The canonical way to test that the HTML5 <template> tag is supported
    supportsTemplateTag = 'content' in document.createElement('template');

function getWrap(tags) {
    var m = tags.match(/^<([a-z]+)[ >]/);
    return (m && lookup[m[1]]) || none;
}

function simpleHtmlParse(html, documentContext) {
    documentContext || (documentContext = document);
    var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;

    // Based on jQuery's "clean" function, but only accounting for table-related elements.
    // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly

    // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
    // a descendant node. For example: "
abc
" will get parsed as "
abc
"
    // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
    // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.

    // Trim whitespace, otherwise indexOf won't work as expected
    var tags = stringTrim(html).toLowerCase(), div = documentContext.createElement("div"),
        wrap = getWrap(tags),
        depth = wrap[0];

    // Go to html and back, then peel off extra wrappers
    // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
    var markup = "ignored
" + wrap[1] + html + wrap[2] + "
";
    if (typeof windowContext['innerShiv'] == "function") {
        // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
        // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
        // somehow shims the native APIs so it just works anyway)
        div.appendChild(windowContext['innerShiv'](markup));
    } else {
        div.innerHTML = markup;
    }

    // Move to the right depth
    while (depth--)
        div = div.lastChild;

    return makeArray(div.lastChild.childNodes);
}

function templateHtmlParse(html, documentContext) {
    if (!documentContext) { documentContext = document; }
    var template = documentContext.createElement('template');
    template.innerHTML = html;
    return makeArray(template.content.childNodes);
}

function jQueryHtmlParse(html, documentContext) {
    // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
    if (jQueryInstance.parseHTML) {
        return jQueryInstance.parseHTML(html, documentContext) || []; // Ensure we always return an array and never null
    } else {
        // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
        var elems = jQueryInstance.clean([html], documentContext);

        // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
        // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
        // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
        if (elems && elems[0]) {
            // Find the top-most parent element that's a direct child of a document fragment
            var elem = elems[0];
            while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
                elem = elem.parentNode;
            // ... then detach it
            if (elem.parentNode)
                elem.parentNode.removeChild(elem);
        }

        return elems;
    }
}

/**
 * parseHtmlFragment converts a string into an array of DOM Nodes.
 * If supported, it uses <template>-tag parsing, falling back on
 * jQuery parsing (if jQuery is present), and finally on a
 * straightforward parser.
 *
 * @param  {string} html            To be parsed.
 * @param  {Object} documentContext That owns the executing code.
 * @return {[DOMNode]}              Parsed DOM Nodes
 */
export function parseHtmlFragment(html, documentContext) {
    // Prefer <template>-tag based HTML parsing.
    return supportsTemplateTag ? templateHtmlParse(html, documentContext) :

        // Benefit from jQuery's on old browsers, where possible
        // NOTE: jQuery's HTML parsing fails on element names like tr-*.
        // See: https://github.com/jquery/jquery/pull/1988
        (jQueryInstance ? jQueryHtmlParse(html, documentContext) :

        // ... otherwise, this simple logic will do in most common cases.
        simpleHtmlParse(html, documentContext));
}

/**
  * setHtml empties the node's contents, unwraps the HTML, and
  * sets the node's HTML using jQuery.html or parseHtmlFragment
  *
  * @param {DOMNode} node Node in which HTML needs to be set
  * @param {DOMNode} html HTML to be inserted in node
  * @returns undefined
  */
export function setHtml(node, html) {
    emptyDomNode(node);

    // There's few cases where we would want to display a stringified
    // function, so we unwrap it.
    if (typeof html === 'function') {
        html = html();
    }

    if ((html !== null) && (html !== undefined)) {
        if (typeof html !== 'string')
            html = html.toString();

        // If the browser supports <template> tags, prefer that, as
        // it obviates all the complex workarounds of jQuery.
        //
        // However, jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
        // for example
 elements which are not normally allowed to exist on their own.
        // If you've referenced jQuery (and template tags are not supported) we'll use that rather than duplicating its code.
        if (jQueryInstance && !supportsTemplateTag) {
            jQueryInstance(node).html(html);
        } else {
            // ... otherwise, use KO's own parsing logic.
            var parsedNodes = parseHtmlFragment(html, node.ownerDocument);
            for (var i = 0; i < parsedNodes.length; i++)
                node.appendChild(parsedNodes[i]);
        }
    }
}
<commit_msg>Fix setHtml when applying to a text node<commit_after>//
// HTML-based manipulation
//
import { stringTrim } from '../string.js';
import { makeArray } from '../array.js';
import { emptyDomNode } from './manipulation.js';
import { jQueryInstance } from '../jquery.js';
import * as virtualElements from './virtualElements';

var none = [0, "", ""],
    table = [1, "
", "
"],
    tbody = [2, "

", "

"],
    colgroup = [ 2, "

<colgroup>", "</colgroup>
"],
    tr = [3, "

", "

"],
    select = [1, "<select multiple='multiple'>", "</select>"],
    fieldset = [1, "<fieldset>", "</fieldset>"],
    map = [1, "<map>", "</map>"],
    object = [1, "<object>", "</object>"],
    lookup = {
        'area': map,
        'col': colgroup,
        'colgroup': table,
        'caption': table,
        'legend': fieldset,
        'thead': table,
        'tbody': table,
        'tfoot': table,
        'tr': tbody,
        'td': tr,
        'th': tr,
        'option': select,
        'optgroup': select,
        'param': object
    },

    // The canonical way to test that the HTML5 <template> tag is supported
    supportsTemplateTag = 'content' in document.createElement('template');

function getWrap(tags) {
    var m = tags.match(/^<([a-z]+)[ >]/);
    return (m && lookup[m[1]]) || none;
}

function simpleHtmlParse(html, documentContext) {
    documentContext || (documentContext = document);
    var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;

    // Based on jQuery's "clean" function, but only accounting for table-related elements.
    // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly

    // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
    // a descendant node. For example: "
abc
" will get parsed as "
abc
"
    // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
    // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.

    // Trim whitespace, otherwise indexOf won't work as expected
    var tags = stringTrim(html).toLowerCase(), div = documentContext.createElement("div"),
        wrap = getWrap(tags),
        depth = wrap[0];

    // Go to html and back, then peel off extra wrappers
    // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
    var markup = "ignored
" + wrap[1] + html + wrap[2] + "
";
    if (typeof windowContext['innerShiv'] == "function") {
        // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
        // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
        // somehow shims the native APIs so it just works anyway)
        div.appendChild(windowContext['innerShiv'](markup));
    } else {
        div.innerHTML = markup;
    }

    // Move to the right depth
    while (depth--)
        div = div.lastChild;

    return makeArray(div.lastChild.childNodes);
}

function templateHtmlParse(html, documentContext) {
    if (!documentContext) { documentContext = document; }
    var template = documentContext.createElement('template');
    template.innerHTML = html;
    return makeArray(template.content.childNodes);
}

function jQueryHtmlParse(html, documentContext) {
    // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
    if (jQueryInstance.parseHTML) {
        return jQueryInstance.parseHTML(html, documentContext) || []; // Ensure we always return an array and never null
    } else {
        // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
        var elems = jQueryInstance.clean([html], documentContext);

        // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
        // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
        // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
        if (elems && elems[0]) {
            // Find the top-most parent element that's a direct child of a document fragment
            var elem = elems[0];
            while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
                elem = elem.parentNode;
            // ... then detach it
            if (elem.parentNode)
                elem.parentNode.removeChild(elem);
        }

        return elems;
    }
}

/**
 * parseHtmlFragment converts a string into an array of DOM Nodes.
 * If supported, it uses <template>-tag parsing, falling back on
 * jQuery parsing (if jQuery is present), and finally on a
 * straightforward parser.
 *
 * @param  {string} html            To be parsed.
 * @param  {Object} documentContext That owns the executing code.
 * @return {[DOMNode]}              Parsed DOM Nodes
 */
export function parseHtmlFragment(html, documentContext) {
    // Prefer <template>-tag based HTML parsing.
    return supportsTemplateTag ? templateHtmlParse(html, documentContext) :

        // Benefit from jQuery's on old browsers, where possible
        // NOTE: jQuery's HTML parsing fails on element names like tr-*.
        // See: https://github.com/jquery/jquery/pull/1988
        (jQueryInstance ? jQueryHtmlParse(html, documentContext) :

        // ... otherwise, this simple logic will do in most common cases.
        simpleHtmlParse(html, documentContext));
}

/**
  * setHtml empties the node's contents, unwraps the HTML, and
  * sets the node's HTML using jQuery.html or parseHtmlFragment
  *
  * @param {DOMNode} node Node in which HTML needs to be set
  * @param {DOMNode} html HTML to be inserted in node
  * @returns undefined
  */
export function setHtml(node, html) {
    emptyDomNode(node);

    // There's few cases where we would want to display a stringified
    // function, so we unwrap it.
    if (typeof html === 'function') {
        html = html();
    }

    if ((html !== null) && (html !== undefined)) {
        if (typeof html !== 'string')
            html = html.toString();

        // If the browser supports <template> tags, prefer that, as
        // it obviates all the complex workarounds of jQuery.
        //
        // However, jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
        // for example
 elements which are not normally allowed to exist on their own.
        // If you've referenced jQuery (and template tags are not supported) we'll use that rather than duplicating its code.
        if (jQueryInstance && !supportsTemplateTag) {
            jQueryInstance(node).html(html);
        } else {
            // ... otherwise, use KO's own parsing logic.
            var parsedNodes = parseHtmlFragment(html, node.ownerDocument);

            if (node.nodeType === 8) {
                if (html === null) {
                    virtualElements.emptyNode(node);
                } else {
                    virtualElements.setDomNodeChildren(node, parsedNodes);
                }
            } else {
                for (var i = 0; i < parsedNodes.length; i++)
                    node.appendChild(parsedNodes[i]);
            }
        }
    }
}<|endoftext|>


<commit_before>import React from 'react/addons';
import DomNode from './domnode.js';
const TestUtils = React.addons.TestUtils;

export default class DomNodes {
  static fromRenderedTree(tree) {
    let instance = new DomNodes();
    instance.domNodes = allNodes(tree)
      .filter(DomNode.isDomNode)
      .map(domNode => DomNode.fromRenderedNode(domNode));
    return instance;
  }

  static fromComponent(component) {
    return DomNodes.fromRenderedTree(renderRecursively(component));
  }
}

function _renderIfNeeded(component) {
  if (DomNode.isDomNode(component)) {
    return component;
  }
  const prototypeOfComponent = Reflect.getPrototypeOf(component.type);
  if (Object.is(prototypeOfComponent, React.Component)) {
    return render(component);
  }
}

function renderRecursively(componentToRender) {
  const rendered = render(componentToRender);
  if (rendered.props  && rendered.props.children){
    if (Array.isArray(rendered.props.children)) {
      rendered.props.children = rendered.props.children.map(_renderIfNeeded);
    } else if (!DomNode.isDomNode(rendered.props.children)) {
      rendered.props.children = _renderIfNeeded(rendered.props.children);
    }
  }
  return rendered;
}

function render(componentToRender) {
  const shallowRenderer = TestUtils.createRenderer();
  shallowRenderer.render(componentToRender);
  return shallowRenderer.getRenderOutput();
}

const ensureToBeArray = (mayBeArray) => Array.isArray(mayBeArray) ? mayBeArray : [mayBeArray];
const flatten = (arr, merged) => [...arr, ...merged];

function allChildren({props = {}}) {
  if (!props.children) {
    return [];
  }
  let children = ensureToBeArray(props.children);
  return [...children, ...children.map(allChildren).reduce(flatten)];
}

function allNodes(tree) {
  return [tree, ...allChildren(tree)];
}

//rendersDomNodeAttributeWithValue
//rendersDomNodeWithInnerText

//return (
//

//
//
{name}

//
{description}

//
//

//);
<commit_msg>Defactor a bit more, to make code look more alike.<commit_after>import React from 'react/addons';
import DomNode from './domnode.js';
const TestUtils = React.addons.TestUtils;

export default class DomNodes {
  static fromRenderedTree(tree) {
    let instance = new DomNodes();
    instance.domNodes = allNodes(tree)
      .filter(DomNode.isDomNode)
      .map(domNode => DomNode.fromRenderedNode(domNode));
    return instance;
  }

  static fromComponent(component) {
    return DomNodes.fromRenderedTree(renderRecursively(component));
  }
}

function _renderIfNeeded(component) {
  if (DomNode.isDomNode(component)) {
    return component;
  }
  const prototypeOfComponent = Reflect.getPrototypeOf(component.type);
  if (Object.is(prototypeOfComponent, React.Component)) {
    return render(component);
  }
}

function renderRecursively(componentToRender) {
  const rendered = render(componentToRender);
  if (rendered.props  && rendered.props.children){
    if (Array.isArray(rendered.props.children)) {
      rendered.props.children = rendered.props.children.map(_renderIfNeeded);
    } else {
      rendered.props.children = [rendered.props.children].map(_renderIfNeeded)[0];
    }
  }
  return rendered;
}

function render(componentToRender) {
  const shallowRenderer = TestUtils.createRenderer();
  shallowRenderer.render(componentToRender);
  return shallowRenderer.getRenderOutput();
}

const ensureToBeArray = (mayBeArray) => Array.isArray(mayBeArray) ? mayBeArray : [mayBeArray];
const flatten = (arr, merged) => [...arr, ...merged];

function allChildren({props = {}}) {
  if (!props.children) {
    return [];
  }
  let children = ensureToBeArray(props.children);
  return [...children, ...children.map(allChildren).reduce(flatten)];
}

function allNodes(tree) {
  return [tree, ...allChildren(tree)];
}

//rendersDomNodeAttributeWithValue
//rendersDomNodeWithInnerText

//return (
//

//
//
{name}

//
{description}

//
//

//);
<|endoftext|>


<commit_before>import { mergeDeep } from '../helpers';

const defaultConfig = {
  eduAPI: {
    assignments: 'assignments/',
    programs: 'programs/',
    students: 'students/classrooms/',
    teachers: 'teachers/classrooms/',
    users: 'users/'
  },
  routes: {
    students: 'students/',
    loginPrompt: '/login'
  },

};

const envConfig = {
  staging: {
    eduAPI: {
      root:'https://education-api-staging.zooniverse.org/',
      programId: '4'
    },
    routes: {
      root:'http://localhost:3000/'
    },
    panoptesAppId: '24ad5676d5d25c6aa850dc5d5f63ec8c03dbc7ae113b6442b8571fce6c5b974c',
    panoptesReturnUrl: 'http://localhost:3000/'
  },
  production: {
    eduAPI: {
      root: 'https://education-api.zooniverse.org/',
      programId: '' // TODO add program id for wildcam gorongosa when it exists in production
    },
    routes: {
      root: 'https://lab.wildcamgorongosa.org/'
    },
    panoptesAppId: '5de4d5dc1566a34e6ea1aafe18a6045f16af62bc58475cd23ed6c4b295425ae4',
    panoptesReturnUrl: 'https://lab.wildcamgorongosa.org/'
  }
};

export default mergeDeep(defaultConfig, envConfig[process.env.NODE_ENV]);
<commit_msg>Update config file to support env switches<commit_after>/*
Configuration Settings
----------------------

The config settings change depending on which environment the app is running in.
By default, this is the development environment, but this can be changed either by:

- An env query string, e.g. localhost?env=production
  (This changes the Panoptes JS Client does)
- The NODE_ENV environment variable on the system running the app.

 */

var DEFAULT_ENV = 'development';
var envFromBrowser = locationMatch(/\W?env=(\w+)/);
var envFromShell = process.env.NODE_ENV;
var env = envFromBrowser || envFromShell || DEFAULT_ENV;

if (!env.match(/^(production|staging|development)$/)) {
  throw new Error(`Error: Invalid Environment - ${envFromShell}`);
}

import { mergeDeep } from '../helpers';

const defaultConfig = {
  eduAPI: {
    assignments: 'assignments/',
    programs: 'programs/',
    students: 'students/classrooms/',
    teachers: 'teachers/classrooms/',
    users: 'users/'
  },
  routes: {
    students: 'students/',
    loginPrompt: '/login'
  },

};

const envConfig = {
  staging: {
    eduAPI: {
      root:'https://education-api-staging.zooniverse.org/',
      programId: '4'
    },
    routes: {
      root:'http://localhost:3000/'
    },
    panoptesAppId: '24ad5676d5d25c6aa850dc5d5f63ec8c03dbc7ae113b6442b8571fce6c5b974c',
    panoptesReturnUrl: 'http://localhost:3000/'
  },
  production: {
    eduAPI: {
      root: 'https://education-api.zooniverse.org/',
      programId: '' // TODO add program id for wildcam gorongosa when it exists in production
    },
    routes: {
      root: 'https://lab.wildcamgorongosa.org/'
    },
    panoptesAppId: '5de4d5dc1566a34e6ea1aafe18a6045f16af62bc58475cd23ed6c4b295425ae4',
    panoptesReturnUrl: 'https://lab.wildcamgorongosa.org/'
  }
};
envConfig.development = envConfig.staging;

export default mergeDeep(defaultConfig, envConfig[env]);
export { env };

// Try and match the location.search property against a regex. Basically mimics
// the CoffeeScript existential operator, in case we're not in a browser.
function locationMatch(regex) {
  var match;
  if (typeof location !== 'undefined' && location !== null) {
    match = location.search.match(regex);
  }
  return (match && match[1]) ? match[1] : undefined;
}
<|endoftext|>


<commit_before>/**
 * Controller for main screen, contains dashboard list as tiles
 * @view views/home.html
 */
(function() {
    'use strict';

    function DashboardListCtrl($scope, $location, $routeParams, Connector, Error, CONST, Lang, Filters, Storage) {
        var _this = this;
        var settings = Storage.getAppSettings();
        var icons = CONST.icons;
        this.search = "";
        var itemDescs = [];
        Filters.clear();

        this.getFolderItems = getFolderItems;
        this.setCurrentFolder = setCurrentFolder;
        this.retrieveData = retrieveData;
        this.requestData = requestData;

        $scope.model = {
            editing: false,
            colors: CONST.bgColorClasses,
            fontColors: settings.isMetro ? CONST.fontColorsMetro : CONST.fontColors,
            icons: icons,
            edItem: null,
            optW: 0,
            optH: 0,
            opt: false
        };
        $scope.tilesOpt = {
            minRows: 4,
            margins: [10,10],
            draggable: {
                enabled: false
            },
            resizable: {
                enabled: false
            }
        };
        $scope.enableEditing = enableEditing;
        $scope.onItemClick = onItemClicked;
        $scope.setTileColor = setTileColor;
        $scope.setFontColor = setFontColor;
        $scope.setIcon = setIcon;
        $scope.getDesc = getDesc;
        $scope.$on("refresh", function() {_this.requestData();});
        $scope.$on("search:dashboard", onSearchDashboard);

        this.setCurrentFolder();
        this.requestData();

        /**
         * Save tiles configuration to storage
         */
        function saveTiles() {
            var t = Storage.getTilesSettings();
            var path = "/";
            if (!settings.hideFolders) path = _this.curFolder;
            for (var i = 0; i < $scope.dashboards.length; i++) {
                var item = $scope.dashboards[i];
                if (!t[path]) t[path] = {};
                if (!t[path][item.title]) t[path][item.title] = {};
                t[path][item.title].row = item.row;
                t[path][item.title].col = item.col;
                t[path][item.title].sizeX = item.sizeX;
                t[path][item.title].sizeY = item.sizeY;
                t[path][item.title].color = item.color;
                t[path][item.title].fontColor = item.fontColor;
                t[path][item.title].icon = item.icon;
                if (item.widget !== null && item.widget !== "") t[path][item.title].widget = parseInt(item.widget); else delete t[path][item.title].widget;

                if (item.customTitle !== undefined) t[path][item.title].title = item.customTitle;
            }

            Storage.setTilesSettings(t);

            // Refresh only if widget id changed
            for (i = 0; i < $scope.dashboards.length; i++) {
                if ($scope.dashboards[i].widget !== $scope.dashboards[i].requestedWidget) {
                    _this.requestData();
                    return;
                }
            }
        }

        /**
         * Get full description of tile item
         * @param {number} idx Item index
         * @returns {object} Item description
         */
        function getDesc(idx) {
            return itemDescs[idx];
        }

        /**
         * Set tile background color
         * @param {number} c Color index. Colors stored in CONST.bgColorClasses
         */
        function setTileColor(c) {
            if (!$scope.model.edItem) return;
            $scope.model.edItem.color = c;
        }

        /**
         * Set tile font color
         * @param {number} c Color index. Colors stored in CONST.fontColorsMetro or CONST.fontColors, depending on current ui style
         */
        function setFontColor(c) {
            if (!$scope.model.edItem) return;
            $scope.model.edItem.fontColor = c;
        }

        /**
         * Set tile icon
         * @param {number} c Icon index. Icons stored in CONST.icons
         */
        function setIcon(c) {
            if (!$scope.model.edItem) return;
            $scope.model.edItem.icon = c;
        }

        /**
         * Toggle editing mode
          */
        function enableEditing() {
            $scope.model.editing = !$scope.model.editing;
            $scope.tilesOpt.draggable.enabled = $scope.model.editing;
            $scope.tilesOpt.resizable.enabled = $scope.model.editing;
            if (!$scope.model.editing) {
                $scope.model.edItem = null;
                saveTiles();
            }
        }

        /**
         * Callback for searching on dashboard
         * @param {object} sc Scope
         * @param {string} txt Text to search
         */
        function onSearchDashboard(sc, txt) {
            _this.search = txt;
            if (!sessionStorage.dashboarList) return;
            $scope.dashboards = _this.getFolderItems(JSON.parse(sessionStorage.dashboarList), _this.curFolder);
        }

        /**
         * Set current folder from address hash
         */
        function setCurrentFolder() {
            _this.curFolder = "";
            if ($routeParams.folder) _this.curFolder = $routeParams.folder;
            if (_this.curFolder !== "") {
                if (_this.curFolder[_this.curFolder.length - 1] != "/") _this.curFolder += "/";
            }
        }

        /**
         * Tile click event handler
         * @param {object} item Tile item clicked
         */
        function onItemClicked(item) {
            if ($scope.model.editing) {
                $scope.model.edItem = item;
                return;
            }
            if (item.isFolder) {
                if (item.title === "") {
                    var parts = _this.curFolder.split("/");
                    parts.splice(parts.length - 2, 2);
                    if (parts.length === 0) $location.path("/"); else
                        $location.path("/f/" + parts.join("/"));
                } else {
                    _this.curFolder += item.title;
                    $location.path("/f/" + _this.curFolder);
                }
            } else {
                $location.path("/d/" + item.path);
            }
        }

        /**
         * Request dashboard list
          */
        function requestData() {
            // Request server only first time
            if (!sessionStorage.dashboarList || Connector.firstRun) {
                Connector.getDashboards().then(_this.retrieveData);
            } else {
                $scope.dashboards = _this.getFolderItems(JSON.parse(sessionStorage.dashboarList), _this.curFolder);
            }
        }

        /**
         * Process retrieved dashboard list
         * @param {object} result Server response contains dashboard list
         */
        function retrieveData(result) {
            if (result.data) if (result.data.Error) {
                Error.show(result.data.Error);
                return;
            }
            if (result.data) {
                if (result.data.children.length === 0) Error.show(Lang.get("errNoDashboards"));
                try {
                    sessionStorage.dashboarList = JSON.stringify(result.data);
                } catch (e) {
                    console.error(e);
                }
                $scope.dashboards = _this.getFolderItems(result.data, _this.curFolder);
            }
        }

        /**
         * Process retrieved dashboard list to setup initial values, etc.
         * @param dashboards
         */
        function setupList(dashboards) {
            for (var i = 0; i < dashboards.length; i++) {
                if (!settings.showImages) dashboards[i].Cover = "";

                if (dashboards[i].Cover) {
                    //dashboards[i].Cover = "http://146.185.143.59/" + dashboards[i].Cover;
                    dashboards[i].icon = 0;
                }

                if (dashboards[i].widget !== null) {
                    dashboards[i].Cover = "";
                    dashboards[i].icon = 0;
                    dashboards[i].requestedWidget = dashboards[i].widget;
                    Connector.getWidgets(dashboards[i].path).success(createDataCallback(dashboards[i]));
                }
            }
        }

        /**
         * Creates callback on data request, for widget placed on tile
         * @param {object} widget Widget description object
         * @returns {Function} Callback function
         */
        function createDataCallback(widget) {
            return function(data) {
                retriveWidgetData(data, widget);
            };
        }

        /**
         * Get items for specified folder
         * @param {object} data Dashboard list
         * @param {string} folder Folder name
         * @returns {Array} Items in folder
         */
        function getFolderItems(data, folder) {
            var dashboards = [];
            var i;
            var item;
            var path;
            var c;
            var conf = Storage.getTilesSettings();
            conf = conf[(settings.hideFolders || settings.hideFolders === undefined) ? '\\' : _this.curFolder] || {};

            if (settings.hideFolders || _this.search) {
                for (i = 0; i < data.children.length; i++) {
                    item = data.children[i];
                    if (item.path.toLocaleLowerCase().indexOf(_this.search.toLocaleLowerCase()) === -1) continue;
                    path = item.path;
                    if (path.substr(0, folder.length).toLowerCase() != folder.toLowerCase()) continue;
                    path = path.substr(folder.length, path.length);
                    parts = path.split("/");
                    if (parts.length === 0) continue;
                    item.title = parts[parts.length - 1];
                    item.title = item.title.replace(".dashboard", "");
                    dashboards.push(item);
                }

                for (i = 0; i < dashboards.length; i++) {
                    c = conf[dashboards[i].title] || {};
                    if (c.row !== undefined) dashboards[i].row = c.row; else dashboards[i].row = parseInt(i / 12);
                    if (c.col !== undefined) dashboards[i].col = c.col; else dashboards[i].col = i % 12;

                    dashboards[i].widget = null;
                    dashboards[i].requestedWidget = null;
                    if (c.widget !== undefined) dashboards[i].widget = parseInt(c.widget);
                    dashboards[i].template = "";
                    dashboards[i].sizeX = c.sizeX || 1;
                    dashboards[i].sizeY = c.sizeY || 1;
                    if (c.icon !== undefined) dashboards[i].icon = c.icon; else dashboards[i].icon = (dashboards[i].title === "" ? (icons.length - 1) : dashboards[i].isFolder ? 2 : 1);
                    dashboards[i].color = c.color || (dashboards[i].title === "" ? 2 : dashboards[i].isFolder ? 3 : 1);
                    dashboards[i].fontColor = c.fontColor || 0;
                    if (dashboards[i].widget === null) dashboards[i].customTitle = c.title || dashboards[i].title; else dashboards[i].title = "";
                }

                setupList(dashboards);

                return dashboards;
            }
            var parts;
            function filter(a) {
                return a.title == parts[0];
            }

            for (i = 0; i < data.children.length; i++) {
                item = data.children[i];
                path = item.path;
                if (path.substr(0, folder.length).toLowerCase() != folder.toLowerCase()) continue;
                path = path.substr(folder.length, path.length);
                parts = path.split("/");
                if (parts.length === 0) continue;
                if (parts.length === 1) {
                    if (item.title === "") {
                        item.title = parts[0];
                        item.title = item.title.replace(".dashboard", "");
                    }
                    dashboards.push(item);
                } else {
                    if (dashboards.filter(filter).length === 0) {
                        dashboards.push({title: parts[0], path: "", isFolder: true, Cover: "" });
                    }
                }
            }
            if (_this.curFolder !== "") dashboards.splice(0, 0, {title: "", path: "", isFolder: true, isBack: true, Cover: ""});
            /*for (var i = 0; i < dashboards.length; i++) {
                if (dashboards[i].bookCover) if (dashboards[i].bookCover !== "") {
                    //dashboards[i].bookCover = JSON.parse(dashboards[i].bookCover);
                }
            }*/

            dashboards = dashboards.sort(function (a, b) {
                if (a.isFolder && !b.isFolder) return -1;
                if (b.isFolder && !a.isFolder) return 1;
                if (a.title > b.title) return 1; else return -1;
            });

            for (i = 0; i < dashboards.length; i++) {
                c = conf[dashboards[i].title] || {};
                if (c.row !== undefined) dashboards[i].row = c.row; else dashboards[i].row = parseInt(i / 12);
                if (c.col !== undefined) dashboards[i].col = c.col; else dashboards[i].col = i % 12;

                dashboards[i].widget = null;
                dashboards[i].requestedWidget = null;
                if (c.widget !== undefined) dashboards[i].widget = parseInt(c.widget);
                dashboards[i].template = "";
                dashboards[i].sizeX = c.sizeX || 1;
                dashboards[i].sizeY = c.sizeY || 1;
                if (c.icon !== undefined) dashboards[i].icon = c.icon; else dashboards[i].icon = (dashboards[i].title === "" ? (icons.length - 1) : dashboards[i].isFolder ? 2 : 1);
                dashboards[i].color = c.color || (dashboards[i].title === "" ? 2 : dashboards[i].isFolder ? 3 : 1);
                dashboards[i].fontColor = c.fontColor || 0;
                if (dashboards[i].widget === null) dashboards[i].customTitle = c.title || dashboards[i].title; else dashboards[i].title = "";
            }

            setupList(dashboards);
            return dashboards;
        }

        /**
         * Callback for widget data retrieved
         * @param {object} result Widget data
         * @param {object} tile Tile on which widget is placed
         */
        function retriveWidgetData(result, tile) {
            if (!result.widgets[tile.widget]) {
                console.warn("Can't find widget with index " + tile.widget);
                return;
            }
            // TODO: replace inline with tile. checking (tile != undefined) is enough to know if this widget in inline mode
            result.widgets[tile.widget].inline = true;
            result.widgets[tile.widget].tile = tile;
            itemDescs.push(result.widgets[tile.widget]);
            tile.idx = itemDescs.length - 1;
            tile.template = "src/views/tile.html";
        }
    }

    var dashboard = angular.module('dashboard');
    dashboard.controller('home', ['$scope', '$location', '$routeParams', 'Connector', 'Error', 'CONST', 'Lang', 'Filters', 'Storage', DashboardListCtrl]);

})();<commit_msg>revert title fix changes<commit_after>/**
 * Controller for main screen, contains dashboard list as tiles
 * @view views/home.html
 */
(function() {
    'use strict';

    function DashboardListCtrl($scope, $location, $routeParams, Connector, Error, CONST, Lang, Filters, Storage) {
        var _this = this;
        var settings = Storage.getAppSettings();
        var icons = CONST.icons;
        this.search = "";
        var itemDescs = [];
        Filters.clear();

        this.getFolderItems = getFolderItems;
        this.setCurrentFolder = setCurrentFolder;
        this.retrieveData = retrieveData;
        this.requestData = requestData;

        $scope.model = {
            editing: false,
            colors: CONST.bgColorClasses,
            fontColors: settings.isMetro ? CONST.fontColorsMetro : CONST.fontColors,
            icons: icons,
            edItem: null,
            optW: 0,
            optH: 0,
            opt: false
        };
        $scope.tilesOpt = {
            minRows: 4,
            margins: [10,10],
            draggable: {
                enabled: false
            },
            resizable: {
                enabled: false
            }
        };
        $scope.enableEditing = enableEditing;
        $scope.onItemClick = onItemClicked;
        $scope.setTileColor = setTileColor;
        $scope.setFontColor = setFontColor;
        $scope.setIcon = setIcon;
        $scope.getDesc = getDesc;
        $scope.$on("refresh", function() {_this.requestData();});
        $scope.$on("search:dashboard", onSearchDashboard);

        this.setCurrentFolder();
        this.requestData();

        /**
         * Save tiles configuration to storage
         */
        function saveTiles() {
            var t = Storage.getTilesSettings();
            var path = "/";
            if (!settings.hideFolders) path = _this.curFolder;
            for (var i = 0; i < $scope.dashboards.length; i++) {
                var item = $scope.dashboards[i];
                if (!t[path]) t[path] = {};
                if (!t[path][item.title]) t[path][item.title] = {};
                t[path][item.title].row = item.row;
                t[path][item.title].col = item.col;
                t[path][item.title].sizeX = item.sizeX;
                t[path][item.title].sizeY = item.sizeY;
                t[path][item.title].color = item.color;
                t[path][item.title].fontColor = item.fontColor;
                t[path][item.title].icon = item.icon;
                if (item.widget !== null && item.widget !== "") t[path][item.title].widget = parseInt(item.widget); else delete t[path][item.title].widget;

                if (item.customTitle !== undefined) t[path][item.title].title = item.customTitle;
            }

            Storage.setTilesSettings(t);

            // Refresh only if widget id changed
            for (i = 0; i < $scope.dashboards.length; i++) {
                if ($scope.dashboards[i].widget !== $scope.dashboards[i].requestedWidget) {
                    _this.requestData();
                    return;
                }
            }
        }

        /**
         * Get full description of tile item
         * @param {number} idx Item index
         * @returns {object} Item description
         */
        function getDesc(idx) {
            return itemDescs[idx];
        }

        /**
         * Set tile background color
         * @param {number} c Color index. Colors stored in CONST.bgColorClasses
         */
        function setTileColor(c) {
            if (!$scope.model.edItem) return;
            $scope.model.edItem.color = c;
        }

        /**
         * Set tile font color
         * @param {number} c Color index. Colors stored in CONST.fontColorsMetro or CONST.fontColors, depending on current ui style
         */
        function setFontColor(c) {
            if (!$scope.model.edItem) return;
            $scope.model.edItem.fontColor = c;
        }

        /**
         * Set tile icon
         * @param {number} c Icon index. Icons stored in CONST.icons
         */
        function setIcon(c) {
            if (!$scope.model.edItem) return;
            $scope.model.edItem.icon = c;
        }

        /**
         * Toggle editing mode
          */
        function enableEditing() {
            $scope.model.editing = !$scope.model.editing;
            $scope.tilesOpt.draggable.enabled = $scope.model.editing;
            $scope.tilesOpt.resizable.enabled = $scope.model.editing;
            if (!$scope.model.editing) {
                $scope.model.edItem = null;
                saveTiles();
            }
        }

        /**
         * Callback for searching on dashboard
         * @param {object} sc Scope
         * @param {string} txt Text to search
         */
        function onSearchDashboard(sc, txt) {
            _this.search = txt;
            if (!sessionStorage.dashboarList) return;
            $scope.dashboards = _this.getFolderItems(JSON.parse(sessionStorage.dashboarList), _this.curFolder);
        }

        /**
         * Set current folder from address hash
         */
        function setCurrentFolder() {
            _this.curFolder = "";
            if ($routeParams.folder) _this.curFolder = $routeParams.folder;
            if (_this.curFolder !== "") {
                if (_this.curFolder[_this.curFolder.length - 1] != "/") _this.curFolder += "/";
            }
        }

        /**
         * Tile click event handler
         * @param {object} item Tile item clicked
         */
        function onItemClicked(item) {
            if ($scope.model.editing) {
                $scope.model.edItem = item;
                return;
            }
            if (item.isFolder) {
                if (item.title === "") {
                    var parts = _this.curFolder.split("/");
                    parts.splice(parts.length - 2, 2);
                    if (parts.length === 0) $location.path("/"); else
                        $location.path("/f/" + parts.join("/"));
                } else {
                    _this.curFolder += item.title;
                    $location.path("/f/" + _this.curFolder);
                }
            } else {
                $location.path("/d/" + item.path);
            }
        }

        /**
         * Request dashboard list
          */
        function requestData() {
            // Request server only first time
            if (!sessionStorage.dashboarList || Connector.firstRun) {
                Connector.getDashboards().then(_this.retrieveData);
            } else {
                $scope.dashboards = _this.getFolderItems(JSON.parse(sessionStorage.dashboarList), _this.curFolder);
            }
        }

        /**
         * Process retrieved dashboard list
         * @param {object} result Server response contains dashboard list
         */
        function retrieveData(result) {
            if (result.data) if (result.data.Error) {
                Error.show(result.data.Error);
                return;
            }
            if (result.data) {
                if (result.data.children.length === 0) Error.show(Lang.get("errNoDashboards"));
                try {
                    sessionStorage.dashboarList = JSON.stringify(result.data);
                } catch (e) {
                    console.error(e);
                }
                $scope.dashboards = _this.getFolderItems(result.data, _this.curFolder);
            }
        }

        /**
         * Process retrieved dashboard list to setup initial values, etc.
         * @param dashboards
         */
        function setupList(dashboards) {
            for (var i = 0; i < dashboards.length; i++) {
                if (!settings.showImages) dashboards[i].Cover = "";

                if (dashboards[i].Cover) {
                    //dashboards[i].Cover = "http://146.185.143.59/" + dashboards[i].Cover;
                    dashboards[i].icon = 0;
                }

                if (dashboards[i].widget !== null) {
                    dashboards[i].Cover = "";
                    dashboards[i].icon = 0;
                    dashboards[i].requestedWidget = dashboards[i].widget;
                    Connector.getWidgets(dashboards[i].path).success(createDataCallback(dashboards[i]));
                }
            }
        }

        /**
         * Creates callback on data request, for widget placed on tile
         * @param {object} widget Widget description object
         * @returns {Function} Callback function
         */
        function createDataCallback(widget) {
            return function(data) {
                retriveWidgetData(data, widget);
            };
        }

        /**
         * Get items for specified folder
         * @param {object} data Dashboard list
         * @param {string} folder Folder name
         * @returns {Array} Items in folder
         */
        function getFolderItems(data, folder) {
            var dashboards = [];
            var i;
            var item;
            var path;
            var c;
            var conf = Storage.getTilesSettings();
            conf = conf[(settings.hideFolders || settings.hideFolders === undefined) ? '\\' : _this.curFolder] || {};

            if (settings.hideFolders || _this.search) {
                for (i = 0; i < data.children.length; i++) {
                    item = data.children[i];
                    if (item.path.toLocaleLowerCase().indexOf(_this.search.toLocaleLowerCase()) === -1) continue;
                    path = item.path;
                    if (path.substr(0, folder.length).toLowerCase() != folder.toLowerCase()) continue;
                    path = path.substr(folder.length, path.length);
                    parts = path.split("/");
                    if (parts.length === 0) continue;
                    item.title = parts[parts.length - 1];
                    item.title = item.title.replace(".dashboard", "");
                    dashboards.push(item);
                }

                for (i = 0; i < dashboards.length; i++) {
                    c = conf[dashboards[i].title] || {};
                    if (c.row !== undefined) dashboards[i].row = c.row; else dashboards[i].row = parseInt(i / 12);
                    if (c.col !== undefined) dashboards[i].col = c.col; else dashboards[i].col = i % 12;

                    dashboards[i].widget = null;
                    dashboards[i].requestedWidget = null;
                    if (c.widget !== undefined) dashboards[i].widget = parseInt(c.widget);
                    dashboards[i].template = "";
                    dashboards[i].sizeX = c.sizeX || 1;
                    dashboards[i].sizeY = c.sizeY || 1;
                    if (c.icon !== undefined) dashboards[i].icon = c.icon; else dashboards[i].icon = (dashboards[i].title === "" ? (icons.length - 1) : dashboards[i].isFolder ? 2 : 1);
                    dashboards[i].color = c.color || (dashboards[i].title === "" ? 2 : dashboards[i].isFolder ? 3 : 1);
                    dashboards[i].fontColor = c.fontColor || 0;
                    //if (dashboards[i].widget === null) dashboards[i].customTitle = c.title || dashboards[i].title; else dashboards[i].title = "";
                    dashboards[i].customTitle = c.title || dashboards[i].title;
                }

                setupList(dashboards);

                return dashboards;
            }
            var parts;
            function filter(a) {
                return a.title == parts[0];
            }

            for (i = 0; i < data.children.length; i++) {
                item = data.children[i];
                path = item.path;
                if (path.substr(0, folder.length).toLowerCase() != folder.toLowerCase()) continue;
                path = path.substr(folder.length, path.length);
                parts = path.split("/");
                if (parts.length === 0) continue;
                if (parts.length === 1) {
                    if (item.title === "") {
                        item.title = parts[0];
                        item.title = item.title.replace(".dashboard", "");
                    }
                    dashboards.push(item);
                } else {
                    if (dashboards.filter(filter).length === 0) {
                        dashboards.push({title: parts[0], path: "", isFolder: true, Cover: "" });
                    }
                }
            }
            if (_this.curFolder !== "") dashboards.splice(0, 0, {title: "", path: "", isFolder: true, isBack: true, Cover: ""});
            /*for (var i = 0; i < dashboards.length; i++) {
                if (dashboards[i].bookCover) if (dashboards[i].bookCover !== "") {
                    //dashboards[i].bookCover = JSON.parse(dashboards[i].bookCover);
                }
            }*/

            dashboards = dashboards.sort(function (a, b) {
                if (a.isFolder && !b.isFolder) return -1;
                if (b.isFolder && !a.isFolder) return 1;
                if (a.title > b.title) return 1; else return -1;
            });

            for (i = 0; i < dashboards.length; i++) {
                c = conf[dashboards[i].title] || {};
                if (c.row !== undefined) dashboards[i].row = c.row; else dashboards[i].row = parseInt(i / 12);
                if (c.col !== undefined) dashboards[i].col = c.col; else dashboards[i].col = i % 12;

                dashboards[i].widget = null;
                dashboards[i].requestedWidget = null;
                if (c.widget !== undefined) dashboards[i].widget = parseInt(c.widget);
                dashboards[i].template = "";
                dashboards[i].sizeX = c.sizeX || 1;
                dashboards[i].sizeY = c.sizeY || 1;
                if (c.icon !== undefined) dashboards[i].icon = c.icon; else dashboards[i].icon = (dashboards[i].title === "" ? (icons.length - 1) : dashboards[i].isFolder ? 2 : 1);
                dashboards[i].color = c.color || (dashboards[i].title === "" ? 2 : dashboards[i].isFolder ? 3 : 1);
                dashboards[i].fontColor = c.fontColor || 0;
                //if (dashboards[i].widget === null) dashboards[i].customTitle = c.title || dashboards[i].title; else dashboards[i].title = "";
                dashboards[i].customTitle = c.title || dashboards[i].title;
            }

            setupList(dashboards);
            return dashboards;
        }

        /**
         * Callback for widget data retrieved
         * @param {object} result Widget data
         * @param {object} tile Tile on which widget is placed
         */
        function retriveWidgetData(result, tile) {
            if (!result.widgets[tile.widget]) {
                console.warn("Can't find widget with index " + tile.widget);
                return;
            }
            // TODO: replace inline with tile. checking (tile != undefined) is enough to know if this widget in inline mode
            result.widgets[tile.widget].inline = true;
            result.widgets[tile.widget].tile = tile;
            itemDescs.push(result.widgets[tile.widget]);
            tile.idx = itemDescs.length - 1;
            tile.template = "src/views/tile.html";
        }
    }

    var dashboard = angular.module('dashboard');
    dashboard.controller('home', ['$scope', '$location', '$routeParams', 'Connector', 'Error', 'CONST', 'Lang', 'Filters', 'Storage', DashboardListCtrl]);

})();<|endoftext|>