<commit_before>"use strict";
/**************************************************************************
*          (C) Vrije Universiteit, Amsterdam (the Netherlands)            *
*                                                                         *
* This file is part of AmCAT - The Amsterdam Content Analysis Toolkit     *
*                                                                         *
* AmCAT is free software: you can redistribute it and/or modify it under  *
* the terms of the GNU Affero General Public License as published by the  *
* Free Software Foundation, either version 3 of the License, or (at your  *
* option) any later version.                                              *
*                                                                         *
* AmCAT is distributed in the hope that it will be useful, but WITHOUT    *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public     *
* License for more details.                                               *
*                                                                         *
* You should have received a copy of the GNU Affero General Public        *
* License along with AmCAT.  If not, see <http://www.gnu.org/licenses/>.  *
***************************************************************************/
define([
    "jquery", "query/utils/dates", "query/valuerenderers", "bootstrap",
    "amcat/amcat.datatables"],
    function($, QueryDates, value_renderers){
    var defaults = { "modal": "#articlelist-dialog" };
    var SEARCH_API_URL = "/api/v4/search";
    var ARTICLE_URL_FORMAT = "../articles/{id}";

    var elastic_filters = {
        year: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "year");
        },
        quarter: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "quarter");
        },
        month: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "month");
        },
        week: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "week");
        },
        day: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "day");
        },
        date: function(form_data, value, filters, interval){
            var ranges = [
                QueryDates.get_range_from_form(form_data),
                QueryDates.get_range(value, interval)
            ];

            if (filters._filtered_before_hack){
                // Two date filters are active; we need to find the intersection
                ranges.push(filters);
            }

            var range = QueryDates.merge(ranges);

            return {
                datetype: "between",
                start_date: range.start_date,
                end_date: range.end_date,
                _filtered_before_hack: true
            };
        },
        medium: function(form_data, value){
            return {mediums: [value.id]};
        },
        total: function(){
            return {};
        },
        term: function(form_data, value){
            return {query: value.label};
        },
        set: function(form_data, value){
            return {sets: value.id}
        }
    };

    return function(userOptions){
        var options = $.extend({}, $.extend(defaults, userOptions));
        var $modal = $(options.modal);

        /**
         * Renders popup with a list of articles, based on given filters.
         * @param filters mapping { type -> filter }. For example:
         *        { medium : 1, date: 1402005600000 }
         */
        function articles_popup(form_data, filters){
            var data = $.extend({}, form_data);

            $.each(filters, function(type, value){
                if (elastic_filters[type] !== undefined){
                    // Use elastic mapping function
                    $.extend(data, elastic_filters[type](form_data, value, data));
                } else {
                    // Store raw data as filter
                    data[type] = value;
                }
            });

            // HACK: If a specific set is requested by a user (by clicking on a table cell,
            // for example), override global articleset filter.
            if (filters.set !== undefined){
                delete data["articlesets"];
            }

            var table = $modal.find(".articlelist").find(".dataTables_scrollBody table");

            if (table.length > 0){
                table.DataTable().destroy();
            }

            amcat.datatables.create_rest_table(
                $modal.find(".articlelist").html(""),
                SEARCH_API_URL + "?" + $.param(getSearchFilters(data), true),
                {
                    "setup_callback": function(tbl){
                        tbl.fnSetRowlink(ARTICLE_URL_FORMAT, "new");
                    },
                    datatables_options: {iDisplayLength: 10,  aaSorting: []}
                }
            )
        }

        /**
         * Convert form field / values to their search API counterpart. For
         * example:
         *     mediums -> mediumid
         *     query -> q
         * @param filters
         */
        function getSearchFilters(filters){
            var new_filters = {};

            var field_map = {
                mediums: "mediumid", query: "q", article_ids: "ids",
                start_date: "start_date", end_date: "end_date",
                articlesets: "sets", sets: "sets", on_date: "on_date"
            };

            var value_map = {
                ids: function(aids){ return $.map(aids.split("\n"), $.trim); },
                start_date: value_renderers.date,
                end_date: value_renderers.date
            };

            $.each(field_map, function(k, v){
                if (filters[k] === null || filters[k] === undefined || filters[k] === ""){
                    return;
                }

                new_filters[v] = filters[k];
                if (value_map[v] !== undefined){
                    new_filters[v] = value_map[v](new_filters[v]);
                }
            });

            return new_filters;
        }

        return {
            /**
             * Show article popup with given filters applied.
             *
             * @param filters: mapping of type -> value.
             */
            show: function(form_data, filters){
                filters = (filters === undefined) ? {} : filters;
                articles_popup(form_data, filters);
                $modal.modal("show");
            },
            hide: function(){
                $modal.modal("hide");
            }
        }
    }
});
<commit_msg>Pass project to search API in articlemodal<commit_after>"use strict";
/**************************************************************************
*          (C) Vrije Universiteit, Amsterdam (the Netherlands)            *
*                                                                         *
* This file is part of AmCAT - The Amsterdam Content Analysis Toolkit     *
*                                                                         *
* AmCAT is free software: you can redistribute it and/or modify it under  *
* the terms of the GNU Affero General Public License as published by the  *
* Free Software Foundation, either version 3 of the License, or (at your  *
* option) any later version.                                              *
*                                                                         *
* AmCAT is distributed in the hope that it will be useful, but WITHOUT    *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public     *
* License for more details.                                               *
*                                                                         *
* You should have received a copy of the GNU Affero General Public        *
* License along with AmCAT.  If not, see <http://www.gnu.org/licenses/>.  *
***************************************************************************/
define([
    "jquery", "query/utils/dates", "query/valuerenderers", "bootstrap",
    "amcat/amcat.datatables"],
    function($, QueryDates, value_renderers){
    var defaults = { "modal": "#articlelist-dialog" };
    var SEARCH_API_URL = "/api/v4/search";
    var ARTICLE_URL_FORMAT = "../articles/{id}";

    var elastic_filters = {
        year: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "year");
        },
        quarter: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "quarter");
        },
        month: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "month");
        },
        week: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "week");
        },
        day: function(form_data, value, filters){
            return elastic_filters.date(form_data, value, filters, "day");
        },
        date: function(form_data, value, filters, interval){
            var ranges = [
                QueryDates.get_range_from_form(form_data),
                QueryDates.get_range(value, interval)
            ];

            if (filters._filtered_before_hack){
                // Two date filters are active; we need to find the intersection
                ranges.push(filters);
            }

            var range = QueryDates.merge(ranges);

            return {
                datetype: "between",
                start_date: range.start_date,
                end_date: range.end_date,
                _filtered_before_hack: true
            };
        },
        medium: function(form_data, value){
            return {mediums: [value.id]};
        },
        total: function(){
            return {};
        },
        term: function(form_data, value){
            return {query: value.label};
        },
        set: function(form_data, value){
            return {sets: value.id}
        }
    };

    return function(userOptions){
        var options = $.extend({}, $.extend(defaults, userOptions));
        var $modal = $(options.modal);

        /**
         * Renders popup with a list of articles, based on given filters.
         * @param filters mapping { type -> filter }. For example:
         *        { medium : 1, date: 1402005600000 }
         */
        function articles_popup(form_data, filters){
            var data = $.extend({}, form_data);

            $.each(filters, function(type, value){
                if (elastic_filters[type] !== undefined){
                    // Use elastic mapping function
                    $.extend(data, elastic_filters[type](form_data, value, data));
                } else {
                    // Store raw data as filter
                    data[type] = value;
                }
            });

            // HACK: If a specific set is requested by a user (by clicking on a table cell,
            // for example), override global articleset filter.
            if (filters.set !== undefined){
                delete data["articlesets"];
            }

            var table = $modal.find(".articlelist").find(".dataTables_scrollBody table");

            if (table.length > 0){
                table.DataTable().destroy();
            }

            amcat.datatables.create_rest_table(
                $modal.find(".articlelist").html(""),
                SEARCH_API_URL + "?" + $.param(getSearchFilters(data), true),
                {
                    "setup_callback": function(tbl){
                        tbl.fnSetRowlink(ARTICLE_URL_FORMAT, "new");
                    },
                    datatables_options: {iDisplayLength: 10,  aaSorting: []}
                }
            )
        }

        /**
         * Convert form field / values to their search API counterpart. For
         * example:
         *     mediums -> mediumid
         *     query -> q
         * @param filters
         */
        function getSearchFilters(filters){
            var new_filters = {};

            console.log(filters);

            var field_map = {
                mediums: "mediumid", query: "q", article_ids: "ids",
                start_date: "start_date", end_date: "end_date",
                articlesets: "sets", sets: "sets", on_date: "on_date",
                project: "project"
            };

            var value_map = {
                ids: function(aids){ return $.map(aids.split("\n"), $.trim); },
                start_date: value_renderers.date,
                end_date: value_renderers.date
            };

            $.each(field_map, function(k, v){
                if (filters[k] === null || filters[k] === undefined || filters[k] === ""){
                    return;
                }

                new_filters[v] = filters[k];
                if (value_map[v] !== undefined){
                    new_filters[v] = value_map[v](new_filters[v]);
                }
            });

            return new_filters;
        }

        return {
            /**
             * Show article popup with given filters applied.
             *
             * @param filters: mapping of type -> value.
             */
            show: function(form_data, filters){
                filters = (filters === undefined) ? {} : filters;
                articles_popup(form_data, filters);
                $modal.modal("show");
            },
            hide: function(){
                $modal.modal("hide");
            }
        }
    }
});
<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');

var Errors = require('./errors.js');
var serialize = require('../serialize.js');
var isSameType = require('./lib/is-same-type.js');

/* TODO:

    Need to add more context for non trivial assignments

    objA = objB

    gives a Number != String type error without fieldname context

*/

module.exports = SubTypeChecker;

function SubTypeChecker(meta) {
    this.meta = meta;
}

// TODO: pretty sure this function is needed...
// function getUnionWithoutOptional(unionType) {
//     var newType = null;

//     var newUnions = [];
//     for (var i = 0; i < unionType.unions.length; i++) {
//         var innerType = unionType.unions[i];
//         if (innerType.type === 'valueLiteral' &&
//             innerType.name === 'undefined'
//         ) {
//             continue;
//         }

//         newUnions.push(innerType);
//     }

//     if (newUnions.length === 1) {
//         newType = newUnions[0];
//     } else {
//         newType = cloneJSIG(unionType);
//         newType.unions = newUnions;
//     }

//     return newType;
// }

SubTypeChecker.prototype.checkSubType =
function checkSubType(node, parent, child) {
    assert(node && node.type, 'ast node must have type');
    assert(parent && parent.type, 'parent must have a type');
    assert(child && child.type, 'child must have a type');

    var result;

    // console.log('checkSubType(' + parent.type + ',' + child.type + ')');
    // console.log('parent: ' + this.meta.serializeType(parent));
    // console.log('child: ' + this.meta.serializeType(child));

    if (parent === child) {
        result = null;
    } else if (parent.type === 'typeLiteral') {
        result = this.checkTypeLiteralSubType(node, parent, child);
    } else if (parent.type === 'genericLiteral') {
        result = this.checkGenericLiteralSubType(node, parent, child);
    } else if (parent.type === 'function') {
        result = this.checkFunctionSubType(node, parent, child);
    } else if (parent.type === 'object') {
        result = this.checkObjectSubType(node, parent, child);
    } else if (parent.type === 'valueLiteral') {
        result = this.checkValueLiteralSubType(node, parent, child);
    } else if (parent.type === 'unionType') {
        result = this.checkUnionSubType(node, parent, child);
    } else if (parent.type === 'freeLiteral') {
        return reportTypeMisMatch(node, parent, child);
    } else if (parent.type === 'intersectionType') {
        return this.checkIntersectionSubType(node, parent, child);
    } else if (parent.type === 'tuple') {
        return this.checkTupleSubType(node, parent, child);
    } else {
        throw new Error('not implemented sub type: ' + parent.type);
    }

    assert(typeof result !== 'boolean', 'must return error or null');
    return result;
};

SubTypeChecker.prototype._checkSpecialTypeLiteralSubType =
function _checkSpecialTypeLiteralSubType(node, parent, child) {
    if (parent.name === '%Export%%ModuleExports') {
        return null;
    } else if (parent.name === '%Boolean%%Mixed') {
        return null;
    } else if (parent.name === '%Mixed%%UnknownExportsField') {
        return null;
    } else if (parent.name === '%Void%%UnknownReturn') {
        return null;
    } else if (parent.name === 'Function' && child.type === 'function') {
        return null;
    } else if (parent.name === 'Object' && child.type === 'object') {
        return null;
    }

    if (parent.name === 'Function' && child.type === 'intersectionType') {
        var types = child.intersections;
        for (var i = 0; i < types.length; i++) {
            if (types[i].type === 'function') {
                return null;
            }
        }
    }
};

/*eslint complexity: [2, 30]*/
SubTypeChecker.prototype.checkTypeLiteralSubType =
function checkTypeLiteralSubType(node, parent, child) {
    if (!parent.builtin && !parent.isGeneric) {
        return reportTypeMisMatch(node, parent, child);
    }

    if (parent.isGeneric) {
        if (!child.isGeneric) {
            return reportTypeMisMatch(node, parent, child);
        }

        if (parent.name !== child.name) {
            return reportTypeMisMatch(node, parent, child);
        }

        return null;
    }

    if (this._checkSpecialTypeLiteralSubType(node, parent, child) === null) {
        return null;
    }

    if (child.type !== 'typeLiteral') {
        return reportTypeMisMatch(node, parent, child);
    }

    var name = parent.name;
    if (name === 'Object') {
        // TODO: model that Error inherits from Object
        if (child.name !== name && child.name !== 'Error') {
            return reportTypeMisMatch(node, parent, child);
        }
    } else if (child.name !== name) {
        return reportTypeMisMatch(node, parent, child);
    } else if (child.name === name) {
        return null;
    } else {
        throw new Error('NotImplemented: ' + parent.name);
    }
};

SubTypeChecker.prototype.checkValueLiteralSubType =
function checkValueLiteralSubType(node, parent, child) {
    if (parent.name === 'null' && child.type === 'typeLiteral' &&
        child.builtin && child.name === '%Null%%Default'
    ) {
        return null;
    }

    if (child.type !== 'valueLiteral') {
        return reportTypeMisMatch(node, parent, child);
    }

    var name = parent.name;
    if (name === 'null') {
        if (child.name !== name) {
            return new Error('[Internal] Not null');
        }
    } else if (name === 'undefined') {
        if (child.name !== name) {
            return new Error('[Internal] Not undefined');
        }
    } else if (name === 'string') {
        if (child.name !== 'string') {
            return new Error('[Internal] Not a string');
        }

        if (child.value !== parent.value) {
            return reportTypeMisMatch(node, parent, child);
        }
    } else {
        // console.log('valueLiteral?', {
        //     p: parent,
        //     c: child
        // });
        throw new Error('NotImplemented: ' + parent.type);
    }
};

SubTypeChecker.prototype.checkGenericLiteralSubType =
function checkGenericLiteralSubType(node, parent, child) {
    if (parent.value.name === 'Array' &&
        parent.value.builtin &&
        child.type === 'typeLiteral' &&
        child.name === '%Array%%Empty'
    ) {
        return null;
    }

    if (parent.value.name === 'Object' &&
        parent.value.builtin &&
        child.type === 'typeLiteral' &&
        child.name === '%Object%%Empty'
    ) {
        return null;
    }

    if (parent.value.name === 'Object' &&
        parent.value.builtin &&
        child.type === 'object' &&
        parent.generics[0].name === 'String' &&
        parent.generics[0].builtin
    ) {
        var valueType = parent.generics[1];

        for (var i = 0; i < child.keyValues.length; i++) {
            var pair = child.keyValues[i];

            var err = this.checkSubType(node, valueType, pair.value);
            if (err) {
                return err;
            }
        }

        return null;
    }

    if (child.type !== 'genericLiteral') {
        return reportTypeMisMatch(node, parent, child);
    }

    var isSame = isSameType(parent.value, child.value);
    if (!isSame) {
        return reportTypeMisMatch(node, parent, child);
    }

    if (parent.generics.length !== child.generics.length) {
        throw new Error('generics mismatch');
    }

    for (i = 0; i < parent.generics.length; i++) {
        isSame = isSameType(parent.generics[i], child.generics[i]);

        if (!isSame) {
            return reportTypeMisMatch(node, parent, child);
        }
    }

    return null;
};

SubTypeChecker.prototype.checkFunctionSubType =
function checkFunctionSubType(node, parent, child) {
    if (child.type !== 'function') {
        return reportTypeMisMatch(node, parent, child);
    }

    var err = this.checkSubType(node, parent.result, child.result);
    if (err) {
        return err;
    }

    if (parent.thisArg) {
        if (!child.thisArg) {
            return reportTypeMisMatch(node, parent, child);
        }

        assert(!parent.thisArg.optional, 'do not support optional thisArg');
        assert(!child.thisArg.optional, 'do not support optional thisArg');

        err = this.checkSubType(
            node, parent.thisArg.value, child.thisArg.value
        );
        if (err) {
            return err;
        }
    }

    if (parent.args.length !== child.args.length) {
        return reportTypeMisMatch(node, parent, child);
    }

    for (var i = 0; i < parent.args.length; i++) {
        /* function args must be the same type, aka invariant */
        var isSame = isSameType(parent.args[i].value, child.args[i].value) &&
            parent.args[i].optional === child.args[i].optional;

        if (!isSame) {
            return reportTypeMisMatch(node, parent.args[i], child.args[i]);
        }
    }

    return null;
};

SubTypeChecker.prototype.checkTupleSubType =
function checkTupleSubType(node, parent, child) {
    if (child.type !== 'tuple') {
        return reportTypeMisMatch(node, parent, child);
    }

    if (parent.values.length !== child.values.length) {
        return reportTypeMisMatch(node, parent, child);
    }

    for (var i = 0; i < parent.values.length; i++) {
        var isSame = isSameType(parent.values[i], child.values[i]);

        if (!isSame) {
            return reportTypeMisMatch(node, parent.values[i], child.values[i]);
        }
    }

    return null;
};

SubTypeChecker.prototype.checkObjectSubType =
function checkObjectSubType(node, parent, child) {
    if (child.type !== 'object' && child.type !== 'intersectionType') {
        return reportTypeMisMatch(node, parent, child);
    }

    var minimumFields = 0;
    for (var i = 0; i < parent.keyValues.length; i++) {
        if (!parent.keyValues[i].optional) {
            minimumFields++;
        }
    }

    var childFieldCount = null;
    if (child.type === 'object') {
        childFieldCount = child.keyValues.length;
    } else if (child.type === 'intersectionType') {
        childFieldCount = 0;
        for (i = 0; i < child.intersections.length; i++) {
            var maybeObj = child.intersections[i];
            if (maybeObj.type === 'object') {
                childFieldCount += maybeObj.keyValues.length;
            }
        }
    }

    // If parent has optional keys, then child must have
    // somewhere between "required keys" and "all keys"
    if (childFieldCount < minimumFields) {
        return Errors.IncorrectFieldCount({
            expected: serialize(parent._raw || parent),
            expectedFields: parent.keyValues.length,
            actual: serialize(child._raw || child),
            actualFields: childFieldCount,
            loc: node.loc,
            line: node.loc.start.line
        });
    }

    var err;
    var childIndex = child.buildObjectIndex();
    for (i = 0; i < parent.keyValues.length; i++) {
        // TODO: check key names...
        var pair = parent.keyValues[i];

        var childType = childIndex[pair.key];
        if (!childType && pair.optional) {
            continue;
        }

        if (!childType) {
            return Errors.MissingExpectedField({
                expected: serialize(parent._raw || parent),
                actual: serialize(child._raw || child),
                expectedName: pair.key,
                loc: node.loc,
                line: node.loc.start.line
            });
        }

        err = this.checkSubType(node, pair.value, childType);
        if (err) {
            return err;
        }
    }

    // For all extra keys, they are allowed...
    return null;
};

SubTypeChecker.prototype.checkUnionSubType =
function checkUnionSubType(node, parent, child) {
    // Check to see that child matches ONE of parent.

    var childUnions = child.type === 'unionType' ? child.unions : [child];

    var err;
    var possibleErrors = [];
    var errors = [];
    for (var j = 0; j < childUnions.length; j++) {
        var isBad = true;

        for (var i = 0; i < parent.unions.length; i++) {
            err = this.checkSubType(
                node, parent.unions[i], childUnions[j]
            );
            if (!err) {
                isBad = false;
            } else {
                possibleErrors.push(err);
            }
        }

        if (isBad) {
            for (var k = 0; k < possibleErrors.length; k++) {
                errors.push(possibleErrors[k]);
            }
        }
    }

    if (!isBad) {
        return null;
    }

    var finalErr = Errors.UnionTypeClassMismatch({
        expected: serialize(parent._raw || parent),
        actual: serialize(child._raw || child),
        loc: node.loc,
        line: node.loc.start.line
    });

    finalErr.originalErrors = errors;
    return finalErr;
};

SubTypeChecker.prototype.checkIntersectionSubType =
function checkIntersectionSubType(node, parent, child) {
    /* TODO: pretty errors & branchType */

    // TODO: what if child is intersectionType
    var childTypes = child.type === 'intersectionType' ?
        child.intersections : [child];

    var allTypes = parent.intersections;
    for (var i = 0; i < allTypes.length; i++) {
        var currType = allTypes[i];
        var err = null;
        var isGood = false;

        for (var j = 0; j < childTypes.length; j++) {
            var error = this.checkSubType(node, currType, childTypes[j]);
            if (!err) {
                isGood = true;
            } else {
                err = error;
            }
        }

        if (!isGood) {
            return err;
        }

        // var err = this.checkSubType(node, currType, child);
        // if (err) {
        //     return err;
        // }
    }

    return null;
};

function reportTypeMisMatch(node, parent, child) {
    return Errors.TypeClassMismatch({
        expected: serialize(parent._raw || parent),
        actual: serialize(child._raw || child),
        _parent: parent,
        _child: child,
        loc: node.loc,
        line: node.loc.start.line
    });
}
<commit_msg>sub-type: normalize single vs double quote literals<commit_after>'use strict';

var assert = require('assert');

var Errors = require('./errors.js');
var serialize = require('../serialize.js');
var isSameType = require('./lib/is-same-type.js');

/* TODO:

    Need to add more context for non trivial assignments

    objA = objB

    gives a Number != String type error without fieldname context

*/

module.exports = SubTypeChecker;

function SubTypeChecker(meta) {
    this.meta = meta;
}

// TODO: pretty sure this function is needed...
// function getUnionWithoutOptional(unionType) {
//     var newType = null;

//     var newUnions = [];
//     for (var i = 0; i < unionType.unions.length; i++) {
//         var innerType = unionType.unions[i];
//         if (innerType.type === 'valueLiteral' &&
//             innerType.name === 'undefined'
//         ) {
//             continue;
//         }

//         newUnions.push(innerType);
//     }

//     if (newUnions.length === 1) {
//         newType = newUnions[0];
//     } else {
//         newType = cloneJSIG(unionType);
//         newType.unions = newUnions;
//     }

//     return newType;
// }

SubTypeChecker.prototype.checkSubType =
function checkSubType(node, parent, child) {
    assert(node && node.type, 'ast node must have type');
    assert(parent && parent.type, 'parent must have a type');
    assert(child && child.type, 'child must have a type');

    var result;

    // console.log('checkSubType(' + parent.type + ',' + child.type + ')');
    // console.log('parent: ' + this.meta.serializeType(parent));
    // console.log('child: ' + this.meta.serializeType(child));

    if (parent === child) {
        result = null;
    } else if (parent.type === 'typeLiteral') {
        result = this.checkTypeLiteralSubType(node, parent, child);
    } else if (parent.type === 'genericLiteral') {
        result = this.checkGenericLiteralSubType(node, parent, child);
    } else if (parent.type === 'function') {
        result = this.checkFunctionSubType(node, parent, child);
    } else if (parent.type === 'object') {
        result = this.checkObjectSubType(node, parent, child);
    } else if (parent.type === 'valueLiteral') {
        result = this.checkValueLiteralSubType(node, parent, child);
    } else if (parent.type === 'unionType') {
        result = this.checkUnionSubType(node, parent, child);
    } else if (parent.type === 'freeLiteral') {
        return reportTypeMisMatch(node, parent, child);
    } else if (parent.type === 'intersectionType') {
        return this.checkIntersectionSubType(node, parent, child);
    } else if (parent.type === 'tuple') {
        return this.checkTupleSubType(node, parent, child);
    } else {
        throw new Error('not implemented sub type: ' + parent.type);
    }

    assert(typeof result !== 'boolean', 'must return error or null');
    return result;
};

SubTypeChecker.prototype._checkSpecialTypeLiteralSubType =
function _checkSpecialTypeLiteralSubType(node, parent, child) {
    if (parent.name === '%Export%%ModuleExports') {
        return null;
    } else if (parent.name === '%Boolean%%Mixed') {
        return null;
    } else if (parent.name === '%Mixed%%UnknownExportsField') {
        return null;
    } else if (parent.name === '%Void%%UnknownReturn') {
        return null;
    } else if (parent.name === 'Function' && child.type === 'function') {
        return null;
    } else if (parent.name === 'Object' && child.type === 'object') {
        return null;
    }

    if (parent.name === 'Function' && child.type === 'intersectionType') {
        var types = child.intersections;
        for (var i = 0; i < types.length; i++) {
            if (types[i].type === 'function') {
                return null;
            }
        }
    }
};

/*eslint complexity: [2, 30]*/
SubTypeChecker.prototype.checkTypeLiteralSubType =
function checkTypeLiteralSubType(node, parent, child) {
    if (!parent.builtin && !parent.isGeneric) {
        return reportTypeMisMatch(node, parent, child);
    }

    if (parent.isGeneric) {
        if (!child.isGeneric) {
            return reportTypeMisMatch(node, parent, child);
        }

        if (parent.name !== child.name) {
            return reportTypeMisMatch(node, parent, child);
        }

        return null;
    }

    if (this._checkSpecialTypeLiteralSubType(node, parent, child) === null) {
        return null;
    }

    if (child.type !== 'typeLiteral') {
        return reportTypeMisMatch(node, parent, child);
    }

    var name = parent.name;
    if (name === 'Object') {
        // TODO: model that Error inherits from Object
        if (child.name !== name && child.name !== 'Error') {
            return reportTypeMisMatch(node, parent, child);
        }
    } else if (child.name !== name) {
        return reportTypeMisMatch(node, parent, child);
    } else if (child.name === name) {
        return null;
    } else {
        throw new Error('NotImplemented: ' + parent.name);
    }
};

SubTypeChecker.prototype.checkValueLiteralSubType =
function checkValueLiteralSubType(node, parent, child) {
    if (parent.name === 'null' && child.type === 'typeLiteral' &&
        child.builtin && child.name === '%Null%%Default'
    ) {
        return null;
    }

    if (child.type !== 'valueLiteral') {
        return reportTypeMisMatch(node, parent, child);
    }

    var name = parent.name;
    if (name === 'null') {
        if (child.name !== name) {
            return new Error('[Internal] Not null');
        }
    } else if (name === 'undefined') {
        if (child.name !== name) {
            return new Error('[Internal] Not undefined');
        }
    } else if (name === 'string') {
        if (child.name !== 'string') {
            return new Error('[Internal] Not a string');
        }

        var childValue = child.value.replace(/\'/g, '"');
        var parentValue = parent.value.replace(/\'/g, '"');

        if (childValue !== parentValue) {
            return reportTypeMisMatch(node, parent, child);
        }
    } else {
        // console.log('valueLiteral?', {
        //     p: parent,
        //     c: child
        // });
        throw new Error('NotImplemented: ' + parent.type);
    }
};

SubTypeChecker.prototype.checkGenericLiteralSubType =
function checkGenericLiteralSubType(node, parent, child) {
    if (parent.value.name === 'Array' &&
        parent.value.builtin &&
        child.type === 'typeLiteral' &&
        child.name === '%Array%%Empty'
    ) {
        return null;
    }

    if (parent.value.name === 'Object' &&
        parent.value.builtin &&
        child.type === 'typeLiteral' &&
        child.name === '%Object%%Empty'
    ) {
        return null;
    }

    if (parent.value.name === 'Object' &&
        parent.value.builtin &&
        child.type === 'object' &&
        parent.generics[0].name === 'String' &&
        parent.generics[0].builtin
    ) {
        var valueType = parent.generics[1];

        for (var i = 0; i < child.keyValues.length; i++) {
            var pair = child.keyValues[i];

            var err = this.checkSubType(node, valueType, pair.value);
            if (err) {
                return err;
            }
        }

        return null;
    }

    if (child.type !== 'genericLiteral') {
        return reportTypeMisMatch(node, parent, child);
    }

    var isSame = isSameType(parent.value, child.value);
    if (!isSame) {
        return reportTypeMisMatch(node, parent, child);
    }

    if (parent.generics.length !== child.generics.length) {
        throw new Error('generics mismatch');
    }

    for (i = 0; i < parent.generics.length; i++) {
        isSame = isSameType(parent.generics[i], child.generics[i]);

        if (!isSame) {
            return reportTypeMisMatch(node, parent, child);
        }
    }

    return null;
};

SubTypeChecker.prototype.checkFunctionSubType =
function checkFunctionSubType(node, parent, child) {
    if (child.type !== 'function') {
        return reportTypeMisMatch(node, parent, child);
    }

    var err = this.checkSubType(node, parent.result, child.result);
    if (err) {
        return err;
    }

    if (parent.thisArg) {
        if (!child.thisArg) {
            return reportTypeMisMatch(node, parent, child);
        }

        assert(!parent.thisArg.optional, 'do not support optional thisArg');
        assert(!child.thisArg.optional, 'do not support optional thisArg');

        err = this.checkSubType(
            node, parent.thisArg.value, child.thisArg.value
        );
        if (err) {
            return err;
        }
    }

    if (parent.args.length !== child.args.length) {
        return reportTypeMisMatch(node, parent, child);
    }

    for (var i = 0; i < parent.args.length; i++) {
        /* function args must be the same type, aka invariant */
        var isSame = isSameType(parent.args[i].value, child.args[i].value) &&
            parent.args[i].optional === child.args[i].optional;

        if (!isSame) {
            return reportTypeMisMatch(node, parent.args[i], child.args[i]);
        }
    }

    return null;
};

SubTypeChecker.prototype.checkTupleSubType =
function checkTupleSubType(node, parent, child) {
    if (child.type !== 'tuple') {
        return reportTypeMisMatch(node, parent, child);
    }

    if (parent.values.length !== child.values.length) {
        return reportTypeMisMatch(node, parent, child);
    }

    for (var i = 0; i < parent.values.length; i++) {
        var isSame = isSameType(parent.values[i], child.values[i]);

        if (!isSame) {
            return reportTypeMisMatch(node, parent.values[i], child.values[i]);
        }
    }

    return null;
};

SubTypeChecker.prototype.checkObjectSubType =
function checkObjectSubType(node, parent, child) {
    if (child.type !== 'object' && child.type !== 'intersectionType') {
        return reportTypeMisMatch(node, parent, child);
    }

    var minimumFields = 0;
    for (var i = 0; i < parent.keyValues.length; i++) {
        if (!parent.keyValues[i].optional) {
            minimumFields++;
        }
    }

    var childFieldCount = null;
    if (child.type === 'object') {
        childFieldCount = child.keyValues.length;
    } else if (child.type === 'intersectionType') {
        childFieldCount = 0;
        for (i = 0; i < child.intersections.length; i++) {
            var maybeObj = child.intersections[i];
            if (maybeObj.type === 'object') {
                childFieldCount += maybeObj.keyValues.length;
            }
        }
    }

    // If parent has optional keys, then child must have
    // somewhere between "required keys" and "all keys"
    if (childFieldCount < minimumFields) {
        return Errors.IncorrectFieldCount({
            expected: serialize(parent._raw || parent),
            expectedFields: parent.keyValues.length,
            actual: serialize(child._raw || child),
            actualFields: childFieldCount,
            loc: node.loc,
            line: node.loc.start.line
        });
    }

    var err;
    var childIndex = child.buildObjectIndex();
    for (i = 0; i < parent.keyValues.length; i++) {
        // TODO: check key names...
        var pair = parent.keyValues[i];

        var childType = childIndex[pair.key];
        if (!childType && pair.optional) {
            continue;
        }

        if (!childType) {
            return Errors.MissingExpectedField({
                expected: serialize(parent._raw || parent),
                actual: serialize(child._raw || child),
                expectedName: pair.key,
                loc: node.loc,
                line: node.loc.start.line
            });
        }

        err = this.checkSubType(node, pair.value, childType);
        if (err) {
            return err;
        }
    }

    // For all extra keys, they are allowed...
    return null;
};

SubTypeChecker.prototype.checkUnionSubType =
function checkUnionSubType(node, parent, child) {
    // Check to see that child matches ONE of parent.

    var childUnions = child.type === 'unionType' ? child.unions : [child];

    var err;
    var possibleErrors = [];
    var errors = [];
    for (var j = 0; j < childUnions.length; j++) {
        var isBad = true;

        for (var i = 0; i < parent.unions.length; i++) {
            err = this.checkSubType(
                node, parent.unions[i], childUnions[j]
            );
            if (!err) {
                isBad = false;
            } else {
                possibleErrors.push(err);
            }
        }

        if (isBad) {
            for (var k = 0; k < possibleErrors.length; k++) {
                errors.push(possibleErrors[k]);
            }
        }
    }

    if (!isBad) {
        return null;
    }

    var finalErr = Errors.UnionTypeClassMismatch({
        expected: serialize(parent._raw || parent),
        actual: serialize(child._raw || child),
        loc: node.loc,
        line: node.loc.start.line
    });

    finalErr.originalErrors = errors;
    return finalErr;
};

SubTypeChecker.prototype.checkIntersectionSubType =
function checkIntersectionSubType(node, parent, child) {
    /* TODO: pretty errors & branchType */

    // TODO: what if child is intersectionType
    var childTypes = child.type === 'intersectionType' ?
        child.intersections : [child];

    var allTypes = parent.intersections;
    for (var i = 0; i < allTypes.length; i++) {
        var currType = allTypes[i];
        var err = null;
        var isGood = false;

        for (var j = 0; j < childTypes.length; j++) {
            var error = this.checkSubType(node, currType, childTypes[j]);
            if (!err) {
                isGood = true;
            } else {
                err = error;
            }
        }

        if (!isGood) {
            return err;
        }

        // var err = this.checkSubType(node, currType, child);
        // if (err) {
        //     return err;
        // }
    }

    return null;
};

function reportTypeMisMatch(node, parent, child) {
    return Errors.TypeClassMismatch({
        expected: serialize(parent._raw || parent),
        actual: serialize(child._raw || child),
        _parent: parent,
        _child: child,
        loc: node.loc,
        line: node.loc.start.line
    });
}
<|endoftext|>


<commit_before>/*
==================================================================================================
  Jasy - JavaScript Tooling Refined
  Copyright 2010-2011 Sebastian Werner
--------------------------------------------------------------------------------------------------
  Inspired by: http://www.phpied.com/when-is-a-stylesheet-really-loaded/
==================================================================================================
*/

Module("jasy.io.StyleSheet",
{
	load : (function(global, doc)
	{
		var head = doc.getElementsByTagName('head')[0];

		var testEl = doc.createElement("link");
		if ("onload" in testEl)
		{
			return function(url, callback, context)
			{
				var el = doc.createElement("link");
				el.onload = function()
				{
					el.onload = null;
					callback.call(context||global);
				}
				el.type = "stylesheet";
				el.src = url;

				head.appendChild(el);
			};
		}
		else
		{
			return function(url, callback, context)
			{
				var style = doc.createElement('style');
				style.textContent = '@import "' + url + '"';

				var interval = setInterval(function()
				{
					var succeed = false;
					try
					{
						// MAGIC: only populated when file is loaded
						style.sheet.cssRules;
						succeed = true;
					}
					catch(e){}

					if (succeed)
					{
						callback.call(context||global);
						clearInterval(interval);
					}
				}, 10);

				head.appendChild(style);
			}
		}
	})(this, document)
});
<commit_msg>REplaced with improved version<commit_after>/*
==================================================================================================
  Jasy - JavaScript Tooling Refined
  Copyright 2010-2011 Sebastian Werner
--------------------------------------------------------------------------------------------------
  Inspired by: http://www.phpied.com/when-is-a-stylesheet-really-loaded/
==================================================================================================
*/

Module("jasy.io.StyleSheet",
{
	/**
	 * Loads a stylesheet and fires a callback when the stylesheet is applied to the page.
	 *
	 * Inspired by:
	 * http://www.phpied.com/when-is-a-stylesheet-really-loaded/
	 *
	 * @param {string} url url pointing to the stylesheet
	 * @param {Function} callback callback that fires when stylesheet is loaded
	 * @param {Object} context context in which the callback is being executed. Defaults to global context.
	 */
	load: function(url, callback, context) {

		var head = document.getElementsByTagName('head')[0];

		context = context || window;

		// Use listener to stylesheet list and compare elements
		if (Permutation.isSet("engine", "webkit"))
		{
			var link = document.createElement('link');
			var sheets = document.styleSheets;
			var startPos = sheets.length;

			handle = setInterval(function() {

				for (var i = 0, l = sheets.length; i < l; i++)  {

					if (sheets[i].ownerNode === link) {
						clearInterval(handle);
						callback.call(context);
					}
				}

			}, 10);

			link.rel = "stylesheet";
			link.type = "text/css";
			link.href = url;

			head.appendChild(link);
		}

		// Use style import fallback for buggy GECKO
		else if (Permutation.isSet("engine", "gecko"))
		{
			var style = document.createElement("style");
			style.textContent = "@import '" + url + "'";

			var handle = setInterval(function() {
				try {
					// MAGIC: only populated when file is loaded
					style.sheet.cssRules;
					clearInterval(handle);
					callback.call(context);
				} catch(e) {}

			}, 10);

			head.appendChild(style);
		}

		// Load event only supported by MSIE and OPERA
		else
		{
			var link = document.createElement("link");
			link.onload = function() {
				link.onload = null;
				callback.call(context);
			};

			link.rel = "stylesheet";
			link.type = "text/css";
			link.href = url;

			head.appendChild(link);
		}
	}
});

<|endoftext|>


<commit_before>print("yo! here we go..");

var ball = scene.GetEntityByNameRaw("ball");
print(ball.Id);

var bat_a = scene.GetEntityByNameRaw("bat_a");
var bat_b = scene.GetEntityByNameRaw("bat_b"); //$('bat_b') - how about?

//nice to have in code to reinit correctly after live updates when devving
var t = ball.placeable.transform;
zerovec = new Vector3df();
t.pos = zerovec;
t.rot = zerovec;
ball.placeable.transform = t;

//r.linearVelocity = new Vector3df(1.0, 0, 0); //XXX NOTE: this fails *silently*
var v = new Vector3df();
v.x = 1.0;
v.y = 0.2;
ball.rigidbody.SetLinearVelocity(v);

var speed = 16.0;

function autopilot() {
  var ta = bat_a.placeable.transform;
  var tb = bat_b.placeable.transform;
  var ball_y = ball.placeable.transform.pos.y;
  ta.pos.y = ball_y;
  tb.pos.y = ball_y;
  bat_a.placeable.transform = ta;
  bat_b.placeable.transform = tb;
}

/*function handlecoll(other, pos, nor, dist, imp, newcoll) {
//PhysicsCollision(Scene::Entity* otherEntity, const Vector3df& position, const Vector3df& normal, float distance, float impulse, bool newCollision);
  print("---coll:---");
  print(pos.x + " : " + pos.y);
YAY not needed anymore (?)*/

function update(dt) {
  var placeable = ball.placeable; //note: could and probably should keep refs to these, and not fetch every time
  var rigidbody = ball.rigidbody;

  var velvec = rigidbody.GetLinearVelocity();
  var curdir = velvec.normalize();
  velvec = curdir.mul(speed);
  rigidbody.SetLinearVelocity(velvec);

  autopilot();
}

//ball.rigidbody.PhysicsCollision.connect(handlecoll);
frame.Updated.connect(update);

print("-*-");
<commit_msg>some comments to pong code, after testing bullet settings<commit_after>print("yo! here we go..");

var ball = scene.GetEntityByNameRaw("ball");
print(ball.Id);

var bat_a = scene.GetEntityByNameRaw("bat_a");
var bat_b = scene.GetEntityByNameRaw("bat_b"); //$('bat_b') - how about?

//nice to have in code to reinit correctly after live updates when devving
var t = ball.placeable.transform;
zerovec = new Vector3df();
t.pos = zerovec;
t.rot = zerovec;
ball.placeable.transform = t;

//r.linearVelocity = new Vector3df(1.0, 0, 0); //XXX NOTE: this fails *silently*
//initial vel for the ball
var v = new Vector3df();
v.x = 10.0;
v.y = 2;
ball.rigidbody.SetLinearVelocity(v);

var speed = 16.0;

function autopilot() {
  var ta = bat_a.placeable.transform;
  var tb = bat_b.placeable.transform;
  var ball_y = ball.placeable.transform.pos.y;
  ta.pos.y = ball_y;
  tb.pos.y = ball_y;
  bat_a.placeable.transform = ta;
  bat_b.placeable.transform = tb;
}

/*function handlecoll(other, pos, nor, dist, imp, newcoll) {
//PhysicsCollision(Scene::Entity* otherEntity, const Vector3df& position, const Vector3df& normal, float distance, float impulse, bool newCollision);
  print("---coll:---");
  print(pos.x + " : " + pos.y);
YAY not needed anymore,
but can perhaps put custom dir setting related to bat speeds etc here,
if bullet doesn't do what we want */

function update(dt) {
  /* with rigidbody restitution set to 1.0 this shouldn't theoretically be needed,
     but with even a little mass (and restiturion 2.0) the ball gets really slow after this
  .. so here we just maintain contant speed (and could of course control it in any way) */
  var rigidbody = ball.rigidbody;

  var velvec = rigidbody.GetLinearVelocity();
  var curdir = velvec.normalize();
  velvec = curdir.mul(speed);
  rigidbody.SetLinearVelocity(velvec);

  autopilot();
}

//ball.rigidbody.PhysicsCollision.connect(handlecoll);
frame.Updated.connect(update);

print("-*-");
<|endoftext|>


<commit_before>/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2014 wcm.io
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
(function (angular) {
  "use strict";
  angular.module('io.wcm.config.directives', ["io.wcm.config.templateUrlList", "io.wcm.config.utilities", "io.wcm.config.templates"])
  /**
   * Directive for displaying the filters on the editor page. Wraps internally the CUI.Select widget.
   * Parent Scope provides the model for the filter:
   * {
   *   name: "Filter Application",
   *   filterParameter:"application",
   *   options: [
   *     {
   *      "value": "/apps/viessmann",
   *      "label": "Viessmann Responsive"
   *     }
   *   ]
   * }
   *
   * filterParameter specifies the property name of the parameter, which will be used to apply the filtering
   * Every change of the selection is applied to the "currentFilter" property from the parent scope. The parent scope has a
   * watcher registered to this property and filters the displayed collection of the parameter n every change
   */
    .directive("filterDropDownList", ['templateUrlList', function (templateList) {
      return {
        restrict: "E",
        replace: true,
        scope: {
          model: "=",
          currentFilter: "="
        },
        templateUrl: templateList.filterDropDownList,
        link: function (scope, element, attr) {
          var widget;

          function handleSelectionChange() {
            scope.$apply(function() {
              scope.currentFilter[scope.model.filterParameter] = widget.getValue()
            });
          }

          function initWidget () {
            widget = new CUI.Select({ element: element[0] });
            widget.on("selected", handleSelectionChange);
            widget.on("itemremoved", handleSelectionChange);
          }

          scope.$evalAsync(initWidget);
        }
      }
    }])
  /**
   * Directive to render the "i" button with a popover for the decription of the parameter. Wraps a coral UI Button and
   * CUI.Popover widget. This directive transcludes the popup content elements. Example:
   * <description-popup>
   *   <popup-content>
   *     Description Text
   *   </popup-content>
   * </description-popup>
   *
   */
    .directive("descriptionPopup", ['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "E",
        replace: true,
        templateUrl: templateList.popupContainer,
        transclude: true,
        link: function(scope, element, attr) {
          var widget;

          scope.id = utils.nextUid();
          scope.$evalAsync(function () {
            widget = new CUI.Popover({element: $("coral-Popover", element)});
          });
        }
      }
    }])
  /**
   * Renders the wrapping elements for the coral ui popover. The content itself is transcluded. The content can also contain markup
   *   <popup-content>
   *     Description Text
   *   </popup-content>
   *
   */
    .directive("popupContent", ['templateUrlList', function (templateList) {
      return {
        restrict: "E",
        replace: true,
        transclude: true,
        templateUrl: templateList.popupContent
      }
    }])
  /**
   * General directive for the widget column to edit the parameters value.
   */
    .directive("parameterValue", ['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "A",
        replace: false,
        require: '^form',
        templateUrl: templateList.parameterValue,
        scope: {
          parameter: '=parameterValue',
          type: '@widgetType'
        },
        link: function(scope, element, attr, ctrl) {
          scope.originalType = scope.type;
          scope.newValue = scope.parameter.value;

          function getDisabledType() {
            if (scope.originalType === "checkbox") {
              return "disabledCheckbox";
            } else {
              return "disabled";
            }
          }
          scope.$watch("parameter.inherited", function(newvalue, oldvalue){
            if (newvalue === true || (scope.parameter.locked === true && scope.parameter.lockedInherited === true)) {
              scope.type = getDisabledType();
              scope.newValue = scope.parameter.value;
              scope.parameter.value = scope.parameter.inheritedValue;
            } else {
              scope.type = scope.originalType;
              scope.parameter.value = scope.newValue;
            }
          });
        }
      }
    }])
    .directive("multifield", ['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "E",
        replace: true,
        templateUrl: templateList.textMultifield,
        scope: {
          parameter: '='
        },
        controller: function($scope) {
          $scope.addNewValue = function(value) {
            $scope.$evalAsync(function() {
              var indexOf = utils.indexOfValueObject($scope.values, value);
              $scope.values.splice(indexOf+1, 0, {value: ""});
            });
          };
          $scope.removeValue = function(value) {
            var indexOf = utils.indexOfValueObject($scope.values, value);
            $scope.values.splice(indexOf, 1);
            if ($scope.values.length == 0) {
              $scope.values.push({value: ""});
            }
          };
        },
        link: function (scope, element, attr) {
          scope.values = [];
          if (scope.parameter.value) {
            var stringValues = scope.parameter.value.split(";");
            if (stringValues.length > 0) {
              for (var i = 0; i < stringValues.length; i++) {
                scope.values.push({value: stringValues[i]});
              }
            } else {
              scope.values.push({value: ""});
            }
          } else {
            scope.values.push({value: ""});
          }
          scope.$watch('values', function() {
            var stringValues = _.pluck(scope.values, "value");
            scope.parameter.value = stringValues.join(";");
          }, true);
        }
      }
    }])
  /**
   * Path browser directive to allow select pages from the siteadmin. Utilizes the CUI.PathBrowser
   */
    .directive("pathBrowser",['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "E",
        replace: true,
        templateUrl: templateList.pathBrowser,
        scope: {
          parameter: '=',
          rootPath: "@"
        },
        link: function (scope, element, attr) {
          var widget = new CUI.PathBrowser({element: element,
            optionLoader: utils.loadAutocompleteOptions,
            rootPath: scope.rootPath
          });

          // apply value changes to the model
          widget.dropdownList.on("selected", function() {
            scope.$apply(function() {
              scope.parameter.value = widget.inputElement.val()
            });
          });

          scope.$on("$destroy", function(){
            // remove listeners
            widget.off();
          })
        }
      }
    }]);
})(angular);
<commit_msg>WCMIO-51: "fixed" dependency injection, which caused the error in the minified version of the editor app.<commit_after>/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2014 wcm.io
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
(function (angular) {
  "use strict";
  angular.module('io.wcm.config.directives', ["io.wcm.config.templateUrlList", "io.wcm.config.utilities", "io.wcm.config.templates"])
  /**
   * Directive for displaying the filters on the editor page. Wraps internally the CUI.Select widget.
   * Parent Scope provides the model for the filter:
   * {
   *   name: "Filter Application",
   *   filterParameter:"application",
   *   options: [
   *     {
   *      "value": "/apps/viessmann",
   *      "label": "Viessmann Responsive"
   *     }
   *   ]
   * }
   *
   * filterParameter specifies the property name of the parameter, which will be used to apply the filtering
   * Every change of the selection is applied to the "currentFilter" property from the parent scope. The parent scope has a
   * watcher registered to this property and filters the displayed collection of the parameter n every change
   */
    .directive("filterDropDownList", ['templateUrlList', function (templateList) {
      return {
        restrict: "E",
        replace: true,
        scope: {
          model: "=",
          currentFilter: "="
        },
        templateUrl: templateList.filterDropDownList,
        link: function (scope, element, attr) {
          var widget;

          function handleSelectionChange() {
            scope.$apply(function() {
              scope.currentFilter[scope.model.filterParameter] = widget.getValue()
            });
          }

          function initWidget () {
            widget = new CUI.Select({ element: element[0] });
            widget.on("selected", handleSelectionChange);
            widget.on("itemremoved", handleSelectionChange);
          }

          scope.$evalAsync(initWidget);
        }
      }
    }])
  /**
   * Directive to render the "i" button with a popover for the decription of the parameter. Wraps a coral UI Button and
   * CUI.Popover widget. This directive transcludes the popup content elements. Example:
   * <description-popup>
   *   <popup-content>
   *     Description Text
   *   </popup-content>
   * </description-popup>
   *
   */
    .directive("descriptionPopup", ['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "E",
        replace: true,
        templateUrl: templateList.popupContainer,
        transclude: true,
        link: function(scope, element, attr) {
          var widget;

          scope.id = utils.nextUid();
          scope.$evalAsync(function () {
            widget = new CUI.Popover({element: $("coral-Popover", element)});
          });
        }
      }
    }])
  /**
   * Renders the wrapping elements for the coral ui popover. The content itself is transcluded. The content can also contain markup
   *   <popup-content>
   *     Description Text
   *   </popup-content>
   *
   */
    .directive("popupContent", ['templateUrlList', function (templateList) {
      return {
        restrict: "E",
        replace: true,
        transclude: true,
        templateUrl: templateList.popupContent
      }
    }])
  /**
   * General directive for the widget column to edit the parameters value.
   */
    .directive("parameterValue", ['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "A",
        replace: false,
        require: '^form',
        templateUrl: templateList.parameterValue,
        scope: {
          parameter: '=parameterValue',
          type: '@widgetType'
        },
        link: function(scope, element, attr, ctrl) {
          scope.originalType = scope.type;
          scope.newValue = scope.parameter.value;

          function getDisabledType() {
            if (scope.originalType === "checkbox") {
              return "disabledCheckbox";
            } else {
              return "disabled";
            }
          }
          scope.$watch("parameter.inherited", function(newvalue, oldvalue){
            if (newvalue === true || (scope.parameter.locked === true && scope.parameter.lockedInherited === true)) {
              scope.type = getDisabledType();
              scope.newValue = scope.parameter.value;
              scope.parameter.value = scope.parameter.inheritedValue;
            } else {
              scope.type = scope.originalType;
              scope.parameter.value = scope.newValue;
            }
          });
        }
      }
    }])
    .directive("multifield", ['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "E",
        replace: true,
        templateUrl: templateList.textMultifield,
        scope: {
          parameter: '='
        },
        controller: ["$scope", function($scope) {
          $scope.addNewValue = function(value) {
            $scope.$evalAsync(function() {
              var indexOf = utils.indexOfValueObject($scope.values, value);
              $scope.values.splice(indexOf+1, 0, {value: ""});
            });
          };
          $scope.removeValue = function(value) {
            var indexOf = utils.indexOfValueObject($scope.values, value);
            $scope.values.splice(indexOf, 1);
            if ($scope.values.length == 0) {
              $scope.values.push({value: ""});
            }
          };
        }],
        link: function (scope, element, attr) {
          scope.values = [];
          if (scope.parameter.value) {
            var stringValues = scope.parameter.value.split(";");
            if (stringValues.length > 0) {
              for (var i = 0; i < stringValues.length; i++) {
                scope.values.push({value: stringValues[i]});
              }
            } else {
              scope.values.push({value: ""});
            }
          } else {
            scope.values.push({value: ""});
          }
          scope.$watch('values', function() {
            var stringValues = _.pluck(scope.values, "value");
            scope.parameter.value = stringValues.join(";");
          }, true);
        }
      }
    }])
  /**
   * Path browser directive to allow select pages from the siteadmin. Utilizes the CUI.PathBrowser
   */
    .directive("pathBrowser",['templateUrlList', "EditorUtilities", function (templateList, utils) {
      return {
        restrict: "E",
        replace: true,
        templateUrl: templateList.pathBrowser,
        scope: {
          parameter: '=',
          rootPath: "@"
        },
        link: function (scope, element, attr) {
          var widget = new CUI.PathBrowser({element: element,
            optionLoader: utils.loadAutocompleteOptions,
            rootPath: scope.rootPath
          });

          // apply value changes to the model
          widget.dropdownList.on("selected", function() {
            scope.$apply(function() {
              scope.parameter.value = widget.inputElement.val()
            });
          });

          scope.$on("$destroy", function(){
            // remove listeners
            widget.off();
          })
        }
      }
    }]);
})(angular);
<|endoftext|>


<commit_before>/*var DocWidth = document.body.clientWidth;

function setDivWidth(DocWidth){
    var Tree = document.getElementById('viewport');
    Tree.style.width=DocWidth/3 + "px";
    Tree.style.borderColor= "blue";
    Tree.style.borderStyle= "solid"
    Tree.style.height = "500px"
    var Content = document.getElementById('content');
    Content.style.width=DocWidth*0.6 + "px";
    Content.style.borderStyle="solid";
    Content.style.borderColor="red";
    Content.style.height = "500px";
    var Content = document.getElementById('content');
    Content.style.width=DocWidth*2/3 + "px";
    Content.style.borderStyle="solid";
    Content.style.borderColor="red";
    Content.style.height = document.body.clientHeight;

}
setDivWidth(DocWidth);*/

var Nodes = {}
var Edges = {}

var NodesVisible = {}
var EdgesVisible = {}
var EdgeCount = 0;

var FocusOrder = new Array();

function InitializeGraph(nodes, edges){
    for (i in nodes){
	AddNode(i.name, i.content, i.ID);}
    for (i in edges){
	AddEdge(i.parentID, i.childID);}
}

function Clear(nodeID){
    for(i in NodesVisible){
	UnShowNode(i);}
}

function DrawEverything(){   //SHOW everything if not focused
    for(i in Nodes){
	ShowNode(i);}
    for(i in Edges){
	ShowEdge(i);}
}

function AddFocus(nodeID){
    FocusOrder.push(nodeID);
    if(!(nodeID in NodesVisible))
       ShowNode(nodeID);
    //console.log(Nodes[nodeID].edgesFrom);
    for(i in Nodes[nodeID].edgesFrom){
	if( !(Nodes[nodeID].edgesFrom[i][1] in NodesVisible))
	    ShowNode(Nodes[nodeID].edgesFrom[i][1]);
	ShowEdge(i);
    }
    for(i in Nodes[nodeID].edgesTo){
	if( !(Nodes[nodeID].edgesTo[i][1] in NodesVisible))
	    ShowNode(Nodes[nodeID].edgesTo[i][1]);
	ShowEdge(i);

    }
}

function RemoveFocus(nodeID){
    do{
	var cur = FocusOrder[FocusOrder.length - 1];
	for(i in Nodes[cur].edgesTo){
	    UnShowNode( Nodes[cur].edgesTo[i][1]);
	}
	for(i in Nodes[cur].edgesFrom && !(i in FocusOrder)){
	    UnShowNode( Nodes[cur].edgesFrom[i][1]);
	}
	FocusOrder.pop();
    }while( FocusOrder[FocusOrder.length - 1] != nodeID)

}

function AddNode(name, content, ID){
    Nodes[ID] = {'name': name, 'content': content, 'ID': ID , 'edgesTo': {}, 'edgesFrom': {}}
}

function AddEdge(parentID, childID){
    Edges[EdgeCount] = {'parentID': parentID, 'childID': childID, 'ID': EdgeCount}
    Nodes[parentID].edgesFrom[EdgeCount] = [EdgeCount, childID];
    Nodes[childID].edgesTo[EdgeCount] = [EdgeCount, parentID];
    EdgeCount +=1;
}

function RemoveNode(nodeID){
    for( i in Nodes[nodeID].EdgesTo){
	RemoveEdge(Nodes[nodeID].EdgesTo[i][1], nodeID);
    }
    for( i in Nodes[nodeID].EdgesFrom){
	RemoveEdge(nodeID, Nodes[nodeID].EdgesFrom[i][1]);
    }
    delete Nodes[nodeID];
}

function RemoveEdge(parentID, childID){
    for(i in Edges){
	if( Edges[i].parentID == parentID && Edges[i].childID == childID){
	    delete Edges[i]}
    }
}

function ShowNode(nodeID)
{
    return NodesVisible[nodeID] = sys.addNode(Nodes[nodeID].name, {'color':'black', 'shape':'box', 'label': Nodes[nodeID].name, 'content':Nodes[nodeID].cont, 'ID':nodeID});
}

function ShowEdge(edgeID)
{
    return EdgesVisible[edgeID] = sys.addEdge(NodesVisible[Edges[edgeID].parentID],NodesVisible[Edges[edgeID].childID], {'color':'black', 'directed':true});
}

function UnShowEdge(edgeID)
{
    sys.pruneEdge(EdgesVisible[edgeID]);
    delete EdgesVisible[edgeID];
}

function UnShowNode(nodeID){
    sys.pruneNode(NodesVisible[nodeID]);
    delete NodesVisible[nodeID];
}

var sys = arbor.ParticleSystem(100, 100,1,1,60); //arguments are repulsion, stiffness, friction, gravity, fps, dt, precision
sys.parameters({gravity:false});
sys.renderer = Renderer("#viewport");

AddNode( 'Tree of Knowledge', 'BIscoito', 0);
AddNode( 'Math', 'Ananas', 1);
AddNode('Philosophy', 'hotel',2);
AddNode('Calculus', 'jarros', 3);
AddNode('Economics', 'hotel',4);
AddNode('Literature', 'jarros', 5);
AddNode('Camões', 'hotel',6);
AddNode('Camus', 'jarros', 7);
AddNode('Linear Algebra', 'hotel',8);
AddNode('Matrix', 'jarros', 9);

AddEdge(0,1);
AddEdge(0,2);
AddEdge(0,4);
AddEdge(0,5);

AddEdge(1,8);
AddEdge(1,3);

AddEdge(5,6);
AddEdge(5,7);
AddEdge(8,9);

DrawEverything();
//UnShowEdge(2);
//ShowEdge(2);
//console.log(Edges);
//UnShowEdge(1);
//console.log(Nodes[0].edgesFrom);
Clear();
//Focus(-1);
AddFocus(0);

$("#viewport").mousedown(function(e){
    var pos = $(this).offset();
    var p = {x:e.pageX-pos.left, y:e.pageY-pos.top}
    selected = nearest = dragged = sys.nearest(p);

    if (selected.node !== null && nearest.distance < 20){
        // dragged.node.tempMass = 10000
        dragged.node.fixed = true;
	if( dragged.node.data.ID in FocusOrder)
	    RemoveFocus(dragged.node.data.ID);
	else
	    AddFocus(dragged.node.data.ID);
    }
    return false;
});
<commit_msg>Update<commit_after>/*var DocWidth = document.body.clientWidth;

function setDivWidth(DocWidth){
    var Tree = document.getElementById('viewport');
    Tree.style.width=DocWidth/3 + "px";
    Tree.style.borderColor= "blue";
    Tree.style.borderStyle= "solid"
    Tree.style.height = "500px"
    var Content = document.getElementById('content');
    Content.style.width=DocWidth*0.6 + "px";
    Content.style.borderStyle="solid";
    Content.style.borderColor="red";
    Content.style.height = "500px";
    var Content = document.getElementById('content');
    Content.style.width=DocWidth*2/3 + "px";
    Content.style.borderStyle="solid";
    Content.style.borderColor="red";
    Content.style.height = document.body.clientHeight;

}
setDivWidth(DocWidth);*/

var Nodes = {}
var Edges = {}

var NodesVisible = {}
var EdgesVisible = {}
var EdgeCount = 0;

var FocusOrder = {};
var FocusCounter = 0;
function InitializeGraph(nodes, edges){
    for (i in nodes){
	AddNode(i.name, i.content, i.ID);}
    for (i in edges){
	AddEdge(i.parentID, i.childID);}
}

function Clear(nodeID){
    for(i in NodesVisible){
	UnShowNode(i);}
}

function DrawEverything(){   //SHOW everything if not focused
    for(i in Nodes){
	ShowNode(i);}
    for(i in Edges){
	ShowEdge(i);}
}

function AddFocus(nodeID){
    FocusOrder[FocusCounter] = nodeID;
    FocusCounter+=1;
    if(!(nodeID in NodesVisible))
       ShowNode(nodeID);
    //console.log(Nodes[nodeID].edgesFrom);
    for(i in Nodes[nodeID].edgesFrom){
	if( !(Nodes[nodeID].edgesFrom[i][1] in NodesVisible))
	    ShowNode(Nodes[nodeID].edgesFrom[i][1]);
	ShowEdge(i);
    }
    for(i in Nodes[nodeID].edgesTo){
	if( !(Nodes[nodeID].edgesTo[i][1] in NodesVisible))
	    ShowNode(Nodes[nodeID].edgesTo[i][1]);
	ShowEdge(i);

    }
}

function RemoveFocus(nodeID){

    console.log(nodeID);
    for(i in Nodes[nodeID].edgesFrom){
	var index=false;
	for(o in FocusOrder){
	    if( FocusOrder[o] == Nodes[nodeID].edgesFrom[i][1]){
		index = true;}
	}
	if(index>-1){
	    RemoveFocus(Nodes[nodeID].edgesFrom[i][1]);}
    }

    for(i in Nodes[nodeID].edgesFrom){
	UnShowNode(Nodes[nodeID].edgesFrom[i][1]);
    }

    for(i in Nodes[nodeID].edgesTo){
	var ind=false;
	for(o in FocusOrder){
	    if( FocusOrder[o] == Nodes[nodeID].edgesTo[i][1]){
		ind = true;}
	}
	if(ind==false){
	    RemoveFocus(Nodes[nodeID].edgesTo[i][1]);}

    }

    //ShowNode(nodeID);

    var ox = 0;
    for( i in FocusOrder){
	if(FocusOrder[i] == nodeID)
	    index = ox;
    }
    delete FocusOrder[ox];

}

function AddNode(name, content, ID){
    Nodes[ID] = {'name': name, 'content': content, 'ID': ID , 'edgesTo': {}, 'edgesFrom': {}}
}

function AddEdge(parentID, childID){
    Edges[EdgeCount] = {'parentID': parentID, 'childID': childID, 'ID': EdgeCount}
    Nodes[parentID].edgesFrom[EdgeCount] = [EdgeCount, childID];
    Nodes[childID].edgesTo[EdgeCount] = [EdgeCount, parentID];
    EdgeCount +=1;
}

function RemoveNode(nodeID){
    for( i in Nodes[nodeID].EdgesTo){
	RemoveEdge(Nodes[nodeID].EdgesTo[i][1], nodeID);
    }
    for( i in Nodes[nodeID].EdgesFrom){
	RemoveEdge(nodeID, Nodes[nodeID].EdgesFrom[i][1]);
    }
    delete Nodes[nodeID];
}

function RemoveEdge(parentID, childID){
    for(i in Edges){
	if( Edges[i].parentID == parentID && Edges[i].childID == childID){
	    delete Edges[i]}
    }
}

function ShowNode(nodeID)
{
    return NodesVisible[nodeID] = sys.addNode(Nodes[nodeID].name, {'color':'black', 'shape':'box', 'label': Nodes[nodeID].name, 'content':Nodes[nodeID].cont, 'ID':nodeID});
}

function ShowEdge(edgeID)
{
    return EdgesVisible[edgeID] = sys.addEdge(NodesVisible[Edges[edgeID].parentID],NodesVisible[Edges[edgeID].childID], {'color':'black', 'directed':true});
}

function UnShowEdge(edgeID)
{
    if( edgeID in EdgesVisible){
	sys.pruneEdge(EdgesVisible[edgeID]);
	delete EdgesVisible[edgeID];
    }
}

function UnShowNode(nodeID){
    if(nodeID in NodesVisible){
	sys.pruneNode(NodesVisible[nodeID]);
	delete NodesVisible[nodeID];
    }
}

var sys = arbor.ParticleSystem(100, 100,1,1,60); //arguments are repulsion, stiffness, friction, gravity, fps, dt, precision
sys.parameters({gravity:false});
sys.renderer = Renderer("#viewport");

AddNode( 'Tree of Knowledge', 'BIscoito', 0);
AddNode( 'Math', 'Ananas', 1);
AddNode('Philosophy', 'hotel',2);
AddNode('Calculus', 'jarros', 3);
AddNode('Economics', 'hotel',4);
AddNode('Literature', 'jarros', 5);
AddNode('Camões', 'hotel',6);
AddNode('Camus', 'jarros', 7);
AddNode('Linear Algebra', 'hotel',8);
AddNode('Matrix', 'jarros', 9);

AddEdge(0,1);
AddEdge(0,2);
AddEdge(0,4);
AddEdge(0,5);

AddEdge(1,8);
AddEdge(1,3);

AddEdge(5,6);
AddEdge(5,7);
AddEdge(8,9);

//UnShowEdge(2);
//ShowEdge(2);
//console.log(Edges);
//UnShowEdge(1);
//console.log(Nodes[0].edgesFrom);
Clear();
//Focus(-1);
AddFocus(0);

var lastTimestamp = 0;
$("#viewport").click(function(e){
    var pos = $(this).offset();
    var p = {x:e.pageX-pos.left, y:e.pageY-pos.top}
    selected = nearest = dragged = sys.nearest(p);
    console.log(e);
    console.log(FocusOrder);

    if (selected.node !== null && nearest.distance < 20 && e.timeStamp - lastTimestamp > 10 ){
	lastTimestamp = e.timeStamp;
        // dragged.node.tempMass = 10000
        dragged.node.fixed = true;

	console.log(dragged.node.data.ID);
	var belongs = false;
	for(i in FocusOrder){
	    if( FocusOrder[i] == dragged.node.data.ID)
		belongs = true;
	}
	if( belongs == true && ( !(dragged.node.data.ID == 0))){
	    RemoveFocus(dragged.node.data.ID);
	}
	else if( !(dragged.node.data.ID == 0)) {
	    console.log(dragged.node.data.ID);
	    AddFocus(dragged.node.data.ID);
	}

    }
    e.stopPropagation();
    e.preventDefault();
    return false;
});
<|endoftext|>


<commit_before>/*jslint eqeqeq: false, browser: true, debug: true, onevar: true,
         plusplus: false, newcap: false, */
/*global _: false, window: false, self: false, escape: false, */

/**
 * ripped off from https://github.com/yui/yui3-gallery/blob/master/src/gallery-taglist/js/taglist.js
 *
 * Hope to submit to the gallery either as a giant patch or something. For
 * now, will hack it directly into Bookie .
 */

YUI.add('bookie-tagcontrol', function (Y) {
    ns = Y.namespace('bookie');

    var keymap = {
        downArrow:40,
        upArrow:38,
        enter:13,
        space:32,
        tab:9,
        backspace:8
    };

    /**
     * Class for each Tag item in our list of tags we control
     *
     */
    function Tag(config) {
        Tag.superclass.constructor.apply(this, arguments);
    }

    Tag.NAME = 'tagcontroller-tag';
    Tag.ATTRS = {
        // The actual text of this tag.
        text: {
            required: true
        },
        // The css class we stick on all Tag dom nodes
        cssClass: {
            required: true
        },
        // should we fire the event that we've been added?
        // we don't want to when adding initial tags on page load
        silent: {
            value: false
        }
    };

    Y.extend(Tag, Y.Base, {
        // bind all events for this particular tag
        _bind: function () {
            // if clicked on, remove it
            this.ui.on('click', this.destroy, this);
        },

        // generate the html for our tag to be placed into the control
        _buildui: function () {
            var ui = Y.Node.create('
');

            ui.addClass(this.get('cssClass'));
            ui.setContent(this.get('text'));
            ui.set('title', 'Click to remove...');
            return ui;
        },

        /**
         * Provide specific destroy() implementation
         *
         * When we get told to go away, we need to clean up html as well as
         * our object instance. The controller also wants to know that we're
         * leaving, so make sure we fire an event so it can update itself as
         * well.
         *
         */
        destructor: function () {
            // remove this node
            this.ui.remove();
            Y.fire('tag:removed', {
                target: this
            });
        },

        /**
         * YUI object init method
         *
         */
        initializer: function (cfg, silent) {
            this.ui = this._buildui();
            this._bind();

            if (!this.get('silent')) {
                Y.fire('tag:added', {
                    target: this
                });
            }
        }
    });

    ns.TagControl = Y.Base.create('bookie-tagcontrol', Y.Widget, [], {
        CLONE_CSS: {
            height: '1em',
            left: -9999,
            opacity: 0,
            overflow: 'hidden',
            position: 'absolute',
            top: -9999,
            width: 'auto'
        },

        tpl: {
            main: '

<input/>

<input type="submit" name="filter" value="Go">
',
        },

        /**
         * Add the tag text into our control as a tag object
         *
         * Requires building an instance, adding the UI to the control, and
         * making sure we're updating our srcNode with the list of tags for
         * form submitting later on.
         *
         */
        _add: function (current_text, silent) {
            // only add if there's text here
            current_text = Y.Lang.trim(current_text);

            if (current_text) {
                var input = this.ui.one('input'),
                    parent = input.get('parentNode'),
                    new_tag = new Tag({
                        text: current_text,
                        cssClass: this.getClassName('item'),
                        parent: this.ui,
                        silent: silent
                    });

                // keep this up
                this.get('tags').push(new_tag);

                // add a new li element before the input one
                parent.get('parentNode').insertBefore(new_tag.ui, parent);
                this._sync_tags();

                // fire an event that a new tag was added
                if (!silent) {
                    Y.fire('tag:changed', {
                        target: new_tag
                    });
                }
            }
        },

        /**
         * Handle the user manually adding a tag from the control UI
         *
         * They can add a tag by hitting space or enter from the control since
         * that's basically saying that "hey, done with this tag, let's move
         * on to the next one".
         *
         */
        _added_tag: function () {
            var input = this.ui.one('input'),
                current_text = input.get('value');

            this._add(current_text);

            // clear the input
            input.set('value', '');

            // focus on the input
            input.focus();
        },

        /**
         * Bind all events the control is looking for and processing.
         *
         */
        _bind_events: function () {
            // events to watch out for from our little control
            // tag:added
            // tag:removed
            // focus out (make last word a tag)
            this.ui.delegate(
                'keyup',
                this._parse_input,
                'input',
                this
            );

            // if you click on anywhere within the ui, focus the input box
            this.ui.on('click', function (e) {
                this.ui.one('.' + this.getClassName('input')).focus();
            }, this);

            // if a tag is removed, catch that event and remove it from our
            // knowledge. This event is coming from the tag itself.
            Y.on('tag:removed', this._remove_tag, this);

            // Look at adjusting the size on any value change event including
            // pasting and such.
            this.ui.one('input').on('valueChange', function(e) {
                this._update_input_width(e.newVal);
            }, this);
        },

        /**
         * We need a clone node in order to tell the input how large to be
         *
         */
        _build_clone: function () {
            var clone= Y.Node.create('');
            clone.setContent('');
            clone.setStyles(this.CLONE_CSS);
            // remove attributes so we don't accidentally grab this node in the
            // future
            clone.generateID();
            clone.setAttrs({
                'tabIndex': -1
            });

            this.get('srcNode').get('parentNode').append(clone);
            this.clone = clone;
            this._update_input_width('');
        },

        /**
         * Generate the html layout for our control.
         *
         * We need to make sure we stick on the nice namespaced css classes
         * for our styling to hook into.
         *
         */
        _buildui: function () {
            this.ui = Y.Node.create(this.tpl.main);
            this.ui.one('ul').addClass(this.getClassName('tags'));
            this.ui.one('li').addClass(this.getClassName('item'));
            this.ui.one('li').addClass(this.getClassName('item-input'));
            this.ui.one('input').addClass(this.getClassName('input'));
        },

        /**
         * Check the input for any actions we should take as the user types
         * into the input control.
         *
         */
        _parse_input: function (e) {
            if (e.keyCode == keymap.space || e.keyCode == keymap.enter) {
                // then handle the current input as a tag and clear for a new
                // one
                this._added_tag();
            }
        },

        /**
         * Add any tags that a srcNode already has in it
         *
         */
        _process_existing: function () {
            // check the srcNode for any existing value=""
            var that = this,
                val = Y.Lang.trim(this.get('srcNode').get('value'));
                tags = [];

            if (val.length > 0) {
                tags = val.split(' ');
            }

            if (tags.length > 0) {
                Y.Array.each(tags, function (n) {
                    that._add(n, true);
                });
            }
        },

        /**
         * When a tag is deleted, we need to update our own record of it.
         *
         */
        _remove_tag: function (e) {
            var t = e.target
                tag = t.get('text');
            Y.Array.find(this.get('tags'), function (item, index, list) {
                if (item.get('text') == tag) {
                    var tlist = this.get('tags');
                    tlist.splice(index, 1);
                    this.set('tags', tlist);
                    return true;
                }

            }, this);

            this._sync_tags();
            Y.fire('tag:changed', e);
        },

        /**
         * Sync up the list of tags the control knows about to the srcNode
         * input element.
         *
         */
        _sync_tags: function () {
            // make sure we keep the tags in our list up to date with the
            // input box
            var tag_list = Y.Array.reduce(this.get('tags'), '', function (prev, cur, index, array) {
                if (prev.length > 0) {
                    return [prev, cur.get('text')].join(' ');
                } else {
                    return cur.get('text');
                }
            }, this);

            this.get('srcNode').set('value', tag_list);
        },

        /**
         * Update the css width of the clone node.
         *
         * In the process of page dom manipulation, the width might change based
         * on other nodes showing up and forcing changes due to padding/etc.
         *
         * We'll play safe and just always recalc the width for the clone before
         * we check it's scroll height.
         *
         */
        _update_input_width: function(new_value) {
            // we need to update the clone with the content so it resizes
            this.clone.setContent(new_value);

            // then update the input to be the matching size + a buffer
            this.ui.one('input').setStyle('width', this.clone.get('offsetWidth') + 20);
        },

        /**
         * Override the YUI widget method for binding UI events
         *
         */
        bindUI: function () {
            this._bind_events();
        },

        /**
         * Override the YUI widget method for building out html/rendering.
         *
         */
        renderUI: function () {
            // first start out by hiding the initial input and placing our own
            // in it's place
            var target = this.get('srcNode')
            // make the target a hidden field so it still gets passed to forms
            // submitted
            target.set('type', 'hidden');

            this._buildui();
            var parent = target.get('parentNode');
            this.ui.appendTo(parent);
            this._build_clone();
        },

        /**
         * Override the YUI widget method for init'ing the UI per existing
         * data.
         *
         */
        syncUI: function () {
            // handle init'ing if there are initial values in the input box
            this._process_existing();
        }
    }, {
        ATTRS: {
            /**
             * Tags is a list of Tag object instances we know about
             *
             */
            tags: {
                value: []
            }
        },
    });

}, '0.1.0', { requires: [
    'base', 'widget', 'handlebars', 'array-extras', 'event-valuechange'
] });
<commit_msg>tab should enter for now as well too until we can do autocomplete<commit_after>/*jslint eqeqeq: false, browser: true, debug: true, onevar: true,
         plusplus: false, newcap: false, */
/*global _: false, window: false, self: false, escape: false, */

/**
 * ripped off from https://github.com/yui/yui3-gallery/blob/master/src/gallery-taglist/js/taglist.js
 *
 * Hope to submit to the gallery either as a giant patch or something. For
 * now, will hack it directly into Bookie .
 */

YUI.add('bookie-tagcontrol', function (Y) {
    ns = Y.namespace('bookie');

    var keymap = {
        downArrow:40,
        upArrow:38,
        enter:13,
        space:32,
        tab:9,
        backspace:8
    };

    /**
     * Class for each Tag item in our list of tags we control
     *
     */
    function Tag(config) {
        Tag.superclass.constructor.apply(this, arguments);
    }

    Tag.NAME = 'tagcontroller-tag';
    Tag.ATTRS = {
        // The actual text of this tag.
        text: {
            required: true
        },
        // The css class we stick on all Tag dom nodes
        cssClass: {
            required: true
        },
        // should we fire the event that we've been added?
        // we don't want to when adding initial tags on page load
        silent: {
            value: false
        }
    };

    Y.extend(Tag, Y.Base, {
        // bind all events for this particular tag
        _bind: function () {
            // if clicked on, remove it
            this.ui.on('click', this.destroy, this);
        },

        // generate the html for our tag to be placed into the control
        _buildui: function () {
            var ui = Y.Node.create('
');

            ui.addClass(this.get('cssClass'));
            ui.setContent(this.get('text'));
            ui.set('title', 'Click to remove...');
            return ui;
        },

        /**
         * Provide specific destroy() implementation
         *
         * When we get told to go away, we need to clean up html as well as
         * our object instance. The controller also wants to know that we're
         * leaving, so make sure we fire an event so it can update itself as
         * well.
         *
         */
        destructor: function () {
            // remove this node
            this.ui.remove();
            Y.fire('tag:removed', {
                target: this
            });
        },

        /**
         * YUI object init method
         *
         */
        initializer: function (cfg, silent) {
            this.ui = this._buildui();
            this._bind();

            if (!this.get('silent')) {
                Y.fire('tag:added', {
                    target: this
                });
            }
        }
    });

    ns.TagControl = Y.Base.create('bookie-tagcontrol', Y.Widget, [], {
        CLONE_CSS: {
            height: '1em',
            left: -9999,
            opacity: 0,
            overflow: 'hidden',
            position: 'absolute',
            top: -9999,
            width: 'auto'
        },

        tpl: {
            main: '

<input/>

<input type="submit" name="filter" value="Go">
',
        },

        /**
         * Add the tag text into our control as a tag object
         *
         * Requires building an instance, adding the UI to the control, and
         * making sure we're updating our srcNode with the list of tags for
         * form submitting later on.
         *
         */
        _add: function (current_text, silent) {
            // only add if there's text here
            current_text = Y.Lang.trim(current_text);

            if (current_text) {
                var input = this.ui.one('input'),
                    parent = input.get('parentNode'),
                    new_tag = new Tag({
                        text: current_text,
                        cssClass: this.getClassName('item'),
                        parent: this.ui,
                        silent: silent
                    });

                // keep this up
                this.get('tags').push(new_tag);

                // add a new li element before the input one
                parent.get('parentNode').insertBefore(new_tag.ui, parent);
                this._sync_tags();

                // fire an event that a new tag was added
                if (!silent) {
                    Y.fire('tag:changed', {
                        target: new_tag
                    });
                }
            }
        },

        /**
         * Handle the user manually adding a tag from the control UI
         *
         * They can add a tag by hitting space or enter from the control since
         * that's basically saying that "hey, done with this tag, let's move
         * on to the next one".
         *
         */
        _added_tag: function () {
            var input = this.ui.one('input'),
                current_text = input.get('value');

            this._add(current_text);

            // clear the input
            input.set('value', '');

            // focus on the input
            input.focus();
        },

        /**
         * Bind all events the control is looking for and processing.
         *
         */
        _bind_events: function () {
            // events to watch out for from our little control
            // tag:added
            // tag:removed
            // focus out (make last word a tag)
            this.ui.delegate(
                'keyup',
                this._parse_input,
                'input',
                this
            );

            // if you click on anywhere within the ui, focus the input box
            this.ui.on('click', function (e) {
                this.ui.one('.' + this.getClassName('input')).focus();
            }, this);

            // if a tag is removed, catch that event and remove it from our
            // knowledge. This event is coming from the tag itself.
            Y.on('tag:removed', this._remove_tag, this);

            // Look at adjusting the size on any value change event including
            // pasting and such.
            this.ui.one('input').on('valueChange', function(e) {
                this._update_input_width(e.newVal);
            }, this);
        },

        /**
         * We need a clone node in order to tell the input how large to be
         *
         */
        _build_clone: function () {
            var clone= Y.Node.create('');
            clone.setContent('');
            clone.setStyles(this.CLONE_CSS);
            // remove attributes so we don't accidentally grab this node in the
            // future
            clone.generateID();
            clone.setAttrs({
                'tabIndex': -1
            });

            this.get('srcNode').get('parentNode').append(clone);
            this.clone = clone;
            this._update_input_width('');
        },

        /**
         * Generate the html layout for our control.
         *
         * We need to make sure we stick on the nice namespaced css classes
         * for our styling to hook into.
         *
         */
        _buildui: function () {
            this.ui = Y.Node.create(this.tpl.main);
            this.ui.one('ul').addClass(this.getClassName('tags'));
            this.ui.one('li').addClass(this.getClassName('item'));
            this.ui.one('li').addClass(this.getClassName('item-input'));
            this.ui.one('input').addClass(this.getClassName('input'));
        },

        /**
         * Check the input for any actions we should take as the user types
         * into the input control.
         *
         */
        _parse_input: function (e) {
            if (e.keyCode == keymap.space || e.keyCode == keymap.enter || e.keyCode == keymap.tab) {
                // then handle the current input as a tag and clear for a new
                // one
                this._added_tag();
            }
        },

        /**
         * Add any tags that a srcNode already has in it
         *
         */
        _process_existing: function () {
            // check the srcNode for any existing value=""
            var that = this,
                val = Y.Lang.trim(this.get('srcNode').get('value'));
                tags = [];

            if (val.length > 0) {
                tags = val.split(' ');
            }

            if (tags.length > 0) {
                Y.Array.each(tags, function (n) {
                    that._add(n, true);
                });
            }
        },

        /**
         * When a tag is deleted, we need to update our own record of it.
         *
         */
        _remove_tag: function (e) {
            var t = e.target
                tag = t.get('text');
            Y.Array.find(this.get('tags'), function (item, index, list) {
                if (item.get('text') == tag) {
                    var tlist = this.get('tags');
                    tlist.splice(index, 1);
                    this.set('tags', tlist);
                    return true;
                }

            }, this);

            this._sync_tags();
            Y.fire('tag:changed', e);
        },

        /**
         * Sync up the list of tags the control knows about to the srcNode
         * input element.
         *
         */
        _sync_tags: function () {
            // make sure we keep the tags in our list up to date with the
            // input box
            var tag_list = Y.Array.reduce(this.get('tags'), '', function (prev, cur, index, array) {
                if (prev.length > 0) {
                    return [prev, cur.get('text')].join(' ');
                } else {
                    return cur.get('text');
                }
            }, this);

            this.get('srcNode').set('value', tag_list);
        },

        /**
         * Update the css width of the clone node.
         *
         * In the process of page dom manipulation, the width might change based
         * on other nodes showing up and forcing changes due to padding/etc.
         *
         * We'll play safe and just always recalc the width for the clone before
         * we check it's scroll height.
         *
         */
        _update_input_width: function(new_value) {
            // we need to update the clone with the content so it resizes
            this.clone.setContent(new_value);

            // then update the input to be the matching size + a buffer
            this.ui.one('input').setStyle('width', this.clone.get('offsetWidth') + 20);
        },

        /**
         * Override the YUI widget method for binding UI events
         *
         */
        bindUI: function () {
            this._bind_events();
        },

        /**
         * Override the YUI widget method for building out html/rendering.
         *
         */
        renderUI: function () {
            // first start out by hiding the initial input and placing our own
            // in it's place
            var target = this.get('srcNode')
            // make the target a hidden field so it still gets passed to forms
            // submitted
            target.set('type', 'hidden');

            this._buildui();
            var parent = target.get('parentNode');
            this.ui.appendTo(parent);
            this._build_clone();
        },

        /**
         * Override the YUI widget method for init'ing the UI per existing
         * data.
         *
         */
        syncUI: function () {
            // handle init'ing if there are initial values in the input box
            this._process_existing();
        }
    }, {
        ATTRS: {
            /**
             * Tags is a list of Tag object instances we know about
             *
             */
            tags: {
                value: []
            }
        },
    });

}, '0.1.0', { requires: [
    'base', 'widget', 'handlebars', 'array-extras', 'event-valuechange'
] });
<|endoftext|>