<commit_before>/*
Copyright (C) 2008-2009  Arne Goedeke

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
/**
 * Some helpful utility functions.
 * @namespace
 */
UTIL = new Object();
/**
 * Flexible RegExp-based replace function. Calls a callback for every match and replaced it by the returned string.
 * @param {Object} reg RegExp Object to be used.
 * @param {String} s String to perform the replace on.
 * @param {Function} cb Callback to be called for every match. Parameters to the callback will be the result returned by the call to RegExp.exec and possible extra arguments that were passed to replace.
 * @returns The resulting string.
 */
UTIL.replace = function(reg, s, cb) {
	var res;
	var last = 0;
	var ret = "";
	var extra;

	if (arguments.length > 3) {
		extra = new Array();
		for (var i = 3; i < arguments.length; i++) extra[i-3] = arguments[i];
	}

	while (null != (res = reg.exec(s))) {
		ret += s.substr(last, reg.lastIndex - res[0].length - last);
		ret += (extra ? cb.apply(null, [res].concat(extra)) : cb(res));
		last = reg.lastIndex;
	}

	if (last < s.length) {
		ret += s.substr(last);
	}

	return ret;
};
UTIL.split_replace = function(reg, s, cb) {
	var res;
	var last = 0;
	var ret = new Array();
	var extra;

	if (arguments.length > 3) {
		extra = new Array();
		for (var i = 3; i < arguments.length; i++) extra[i-3] = arguments[i];
	}

	while (null != (res = reg.exec(s))) {
		ret.push(s.substr(last, reg.lastIndex - res[0].length - last));
		ret.push(extra ? cb.apply(null, [res].concat(extra)) : cb(res));
		last = reg.lastIndex;
	}

	if (last < s.length) {
		ret.push(s.substr(last));
	}

	return ret;
};
UTIL.has_prefix = function(s, n) {
	if (s.length < n.length) return false;
	return (n == s.substr(0, n.length));
};
UTIL.has_suffix = function(s, n) {
	if (s.length < n.length) return false;
	return (n == s.substr(s.length-n.length, n.length));
};
UTIL.search_array = function(a, n) {
	for (var i = 0; i < a.length; i++) {
		if (n == a[i]) return i;
	}

	return -1;
};
UTIL.replaceClass = function(o, cl1, cl2) {
	var classes = o.className.length ? o.className.split(' ') : [];
	var i = UTIL.search_array(classes, cl1);
	var j = UTIL.search_array(classes, cl2);

	if (i == -1 && j == -1) {
		if (cl2) classes.push(cl2);
	} else if (i == -1) {
		return;
	} else if (j == -1 && cl2) {
		classes[i] = cl2;
	} else {
		classes.splice(i, 1);
	}
	o.className = classes.join(" ");
};
UTIL.addClass = function(o, cl) {
	var classes = o.className.length ? o.className.split(' ') : [];
	var i = UTIL.search_array(classes, cl);

	if (i == -1) {
		classes.push(cl);
		o.className = classes.join(" ");
	}
};
UTIL.hasClass = function(o, cl) {
	var classes = o.className.split(' ');
	return (-1 != UTIL.search_array(classes, cl));
};
UTIL.removeClass = function(o, cl) {
	UTIL.replaceClass(o, cl);
};
UTIL.url_escape = function(s) {
    	return escape(s).replace(/\+/g, "%2B").replace(/\//g, "%2F");
};
UTIL.make_url = function(url, vars) {
    	var list = [];
	var key;
	if (vars) for (key in vars) if (vars.hasOwnProperty(key)) {
	    list.push(key + "=" + UTIL.url_escape(vars[key]));
	} else return url;

	return url + "?" + list.join("&");
};
<commit_msg>make callback with right context<commit_after>/*
Copyright (C) 2008-2009  Arne Goedeke

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
/**
 * Some helpful utility functions.
 * @namespace
 */
UTIL = new Object();
/**
 * Flexible RegExp-based replace function. Calls a callback for every match and replaced it by the returned string.
 * @param {Object} reg RegExp Object to be used.
 * @param {String} s String to perform the replace on.
 * @param {Function} cb Callback to be called for every match. Parameters to the callback will be the result returned by the call to RegExp.exec and possible extra arguments that were passed to replace.
 * @returns The resulting string.
 */
UTIL.replace = function(reg, s, cb) {
	var res;
	var last = 0;
	var ret = "";
	var extra;

	if (arguments.length > 3) {
		extra = new Array();
		for (var i = 3; i < arguments.length; i++) extra[i-3] = arguments[i];
	}

	while (null != (res = reg.exec(s))) {
		ret += s.substr(last, reg.lastIndex - res[0].length - last);
		ret += (extra ? cb.apply(null, [res].concat(extra)) : cb(res));
		last = reg.lastIndex;
	}

	if (last < s.length) {
		ret += s.substr(last);
	}

	return ret;
};
UTIL.split_replace = function(reg, s, cb) {
	var res;
	var last = 0;
	var ret = new Array();
	var extra;

	if (arguments.length > 3) {
		extra = new Array();
		for (var i = 3; i < arguments.length; i++) extra[i-3] = arguments[i];
	}

	while (null != (res = reg.exec(s))) {
		ret.push(s.substr(last, reg.lastIndex - res[0].length - last));
		ret.push(extra ? cb.apply(null, [res].concat(extra)) : cb(res));
		last = reg.lastIndex;
	}

	if (last < s.length) {
		ret.push(s.substr(last));
	}

	return ret;
};
UTIL.has_prefix = function(s, n) {
	if (s.length < n.length) return false;
	return (n == s.substr(0, n.length));
};
UTIL.has_suffix = function(s, n) {
	if (s.length < n.length) return false;
	return (n == s.substr(s.length-n.length, n.length));
};
UTIL.search_array = function(a, n) {
	for (var i = 0; i < a.length; i++) {
		if (n == a[i]) return i;
	}

	return -1;
};
UTIL.replaceClass = function(o, cl1, cl2) {
	var classes = o.className.length ? o.className.split(' ') : [];
	var i = UTIL.search_array(classes, cl1);
	var j = UTIL.search_array(classes, cl2);

	if (i == -1 && j == -1) {
		if (cl2) classes.push(cl2);
	} else if (i == -1) {
		return;
	} else if (j == -1 && cl2) {
		classes[i] = cl2;
	} else {
		classes.splice(i, 1);
	}
	o.className = classes.join(" ");
};
UTIL.addClass = function(o, cl) {
	var classes = o.className.length ? o.className.split(' ') : [];
	var i = UTIL.search_array(classes, cl);

	if (i == -1) {
		classes.push(cl);
		o.className = classes.join(" ");
	}
};
UTIL.hasClass = function(o, cl) {
	var classes = o.className.split(' ');
	return (-1 != UTIL.search_array(classes, cl));
};
UTIL.removeClass = function(o, cl) {
	UTIL.replaceClass(o, cl);
};
UTIL.url_escape = function(s) {
    	return escape(s).replace(/\+/g, "%2B").replace(/\//g, "%2F");
};
UTIL.make_url = function(url, vars) {
    	var list = [];
	var key;
	if (vars) for (key in vars) if (vars.hasOwnProperty(key)) {
	    list.push(key + "=" + UTIL.url_escape(vars[key]));
	} else return url;

	return url + "?" + list.join("&");
};
UTIL.make_callback = function(obj, fun) {
	return (function() {
	    fun.apply(obj, [ this ].concat(arguments));
	});

};
<|endoftext|>


<commit_before>//CalendarBundle
Mautic.calendarOnLoad = function (container) {
    Mautic.loadCalendarEvents(container);
};

Mautic.calendarModalOnLoad = function (container, response) {
    mQuery('#calendar').fullCalendar( 'refetchEvents' );
    mQuery(container + " a[data-toggle='ajax']").off('click.ajax');
    mQuery(container + " a[data-toggle='ajax']").on('click.ajax', function (event) {
        event.preventDefault();
        mQuery('.modal').modal('hide');
        return Mautic.ajaxifyLink(this, event);
    });
};

Mautic.initializeCalendarModals = function (container) {
    mQuery(container + " *[data-toggle='ajaxmodal']").off('click.ajaxmodal');
    mQuery(container + " *[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
        event.preventDefault();
        Mautic.ajaxifyModal(this, event);
    });
}

Mautic.loadCalendarEvents = function (container) {
    mQuery('#calendar').fullCalendar({
        events: mauticAjaxUrl + "?action=calendar:generateData",
        eventRender: function(event, element) {
            element = mQuery(element);
            if (event.iconClass) {
                element.find('.fc-title').before(mQuery('<i />').addClass(event.iconClass));
            }
            if (event.attr) {
                element.attr(event.attr);
            }
            if (event.description) {
                element.tooltip({'title': event.description});
            }
        },
        loading: function(bool) {
            // if calendar events are loaded
            if (!bool) {
                //initialize ajax'd modals
                Mautic.initializeCalendarModals(container);
            }
        },
        eventDrop: function(event, delta, revertFunc) {
            mQuery.ajax({
                url: mauticAjaxUrl + "?action=calendar:updateEvent",
                data: 'entityId=' + event.entityId + '&entityType=' + event.entityType + '&setter=' + event.setter + '&startDate=' + event.start.format(),
                type: "POST",
                dataType: "json",
                success: function (response) {
                    if (!response.success) {
                        revertFunc();
                    }
                    Mautic.initializeCalendarModals(container);
                    if (response.flashes) {
                        Mautic.setFlashes(response.flashes);
                        Mautic.hideFlashes();
                    }
                },
                error: function (response, textStatus, errorThrown) {
                    revertFunc();
                    Mautic.processAjaxError(response, textStatus, errorThrown, true);
                }
            });
        }
    });
}
<commit_msg>Added limit of events in one day (Calendar)<commit_after>//CalendarBundle
Mautic.calendarOnLoad = function (container) {
    Mautic.loadCalendarEvents(container);
};

Mautic.calendarModalOnLoad = function (container, response) {
    mQuery('#calendar').fullCalendar( 'refetchEvents' );
    mQuery(container + " a[data-toggle='ajax']").off('click.ajax');
    mQuery(container + " a[data-toggle='ajax']").on('click.ajax', function (event) {
        event.preventDefault();
        mQuery('.modal').modal('hide');
        return Mautic.ajaxifyLink(this, event);
    });
};

Mautic.initializeCalendarModals = function (container) {
    mQuery(container + " *[data-toggle='ajaxmodal']").off('click.ajaxmodal');
    mQuery(container + " *[data-toggle='ajaxmodal']").on('click.ajaxmodal', function (event) {
        event.preventDefault();
        Mautic.ajaxifyModal(this, event);
    });
}

Mautic.loadCalendarEvents = function (container) {
    mQuery('#calendar').fullCalendar({
        events: mauticAjaxUrl + "?action=calendar:generateData",
        lang: 'en',
        eventLimit: true,
        eventLimitText: "more",
        eventRender: function(event, element) {
            element = mQuery(element);
            if (event.iconClass) {
                element.find('.fc-title').before(mQuery('<i />').addClass(event.iconClass));
            }
            if (event.attr) {
                element.attr(event.attr);
            }
            if (event.description) {
                element.tooltip({'title': event.description});
            }
        },
        loading: function(bool) {
            // if calendar events are loaded
            if (!bool) {
                //initialize ajax'd modals
                Mautic.initializeCalendarModals(container);
            }
        },
        eventDrop: function(event, delta, revertFunc) {
            mQuery.ajax({
                url: mauticAjaxUrl + "?action=calendar:updateEvent",
                data: 'entityId=' + event.entityId + '&entityType=' + event.entityType + '&setter=' + event.setter + '&startDate=' + event.start.format(),
                type: "POST",
                dataType: "json",
                success: function (response) {
                    if (!response.success) {
                        revertFunc();
                    }
                    Mautic.initializeCalendarModals(container);
                    if (response.flashes) {
                        Mautic.setFlashes(response.flashes);
                        Mautic.hideFlashes();
                    }
                },
                error: function (response, textStatus, errorThrown) {
                    revertFunc();
                    Mautic.processAjaxError(response, textStatus, errorThrown, true);
                }
            });
        }
    });
}
<|endoftext|>


<commit_before>/*$AMPERSAND_VERSION*/
var _ = require('underscore');
var Events = require('backbone-events-standalone');
var classExtend = require('ampersand-class-extend');
var underscoreMixins = require('ampersand-collection-underscore-mixin');
var slice = Array.prototype.slice;

function SubCollection(collection, spec) {
    this._spec = (spec || (spec = {}));
    this.collection = collection;
    this._reset();

    this._setupComparator(spec.comparator);
    this._watch(spec.watched || []);
    this._parseFilters(spec);
    this._runFilters();
    this.listenTo(this.collection, 'all', this._onCollectionEvent);
}

_.extend(SubCollection.prototype, Events, underscoreMixins, {
    // add a filter function directly
    addFilter: function (filter) {
        this.swapFilters([filter], []);
    },

    // remove filter function directly
    removeFilter: function (filter) {
        this.swapFilters([], [filter]);
    },

    // clears filters fires events for changes
    clearFilters: function () {
        this._reset();
        this._runFilters();
    },

    // Swap out a set of old filters with a set of
    // new filters
    swapFilters: function (newFilters, oldFilters) {
        var self = this;

        if (!oldFilters) {
            oldFilters = this._filters;
        } else if (!_.isArray(oldFilters)) {
            oldFilters = [oldFilters];
        }

        if (!newFilters) {
            newFilters = [];
        } else if (!_.isArray(newFilters)) {
            newFilters = [newFilters];
        }

        oldFilters.forEach(function (filter) {
            self._removeFilter(filter);
        });

        newFilters.forEach(function (filter) {
            self._addFilter(filter);
        });

        this._runFilters();
    },

    // Update sub collection config, if `clear`
    // then clear existing filters before start.
    // This takes all the same filter arguments
    // as the init function. So you can pass:
    // {
    //   where: {
    //      name: 'something'
    //   },
    //   limit: 20
    // }
    configure: function (opts, clear) {

        if (clear) {
            this._spec = {};
            delete this.comparator;
            this._resetFilters();
        }
        this._parseFilters(opts);
        this._runFilters();
    },

    // gets a model at a given index
    at: function (index) {
        return this.models[index];
    },

    // proxy `get` method to the underlying collection
    get: function (query, indexName) {
        var model = this.collection.get(query, indexName);
        if (model && this.contains(model)) return model;
    },

    // remove filter if found
    _removeFilter: function (filter) {
        var index = this._filters.indexOf(filter);
        if (index !== -1) {
            this._filters.splice(index, 1);
        }
    },

    // clear all filters, reset everything
    _reset: function () {
        this.models = [];
        this._resetFilters();
    },

    // just reset filters, no model changes
    _resetFilters: function () {
        this._filters = [];
        this._watched = [];
        if (this._spec.comparator) this._setupComparator(this._spec.comparator);
        if (this._spec.watched) this._watch(this._spec.watched);
        this.limit = undefined;
        this.offset = undefined;
    },

    // internal method registering new filter function
    _addFilter: function (filter) {
        this._filters.push(filter);
    },

    // adds a property or array of properties to watch, ensures uniquness.
    _watch: function (item) {
        this._watched = _.union(this._watched, _.isArray(item) ? item : [item]);
    },

    // removes a watched property
    _unwatch: function (item) {
        this._watched = _.without(this._watched, item);
    },

    // adds comparator and adds watch for it if it's a string
    _setupComparator: function (comparator) {
        if (comparator) {
            this.comparator = comparator;
            if (typeof comparator !== 'string') {
                return;
            }
            this._spec.watched = this._spec.watched || (this._spec.watched = []);
            if (this._spec.watched.indexOf(comparator) === -1) {
                this._spec.watched.push(comparator);
            }
        }
    },

    _parseFilters: function (spec) {
        if (spec.where) {
            _.each(spec.where, function (value, item) {
                this._addFilter(function (model) {
                    return (model.get ? model.get(item) : model[item]) === value;
                });
            }, this);
            // also make sure we watch all `where` keys
            this._watch(_.keys(spec.where));
        }
        if (spec.hasOwnProperty('limit')) this.limit = spec.limit;
        if (spec.hasOwnProperty('offset')) this.offset = spec.offset;
        if (spec.filter) {
            this._addFilter(spec.filter, false);
        }
        if (spec.filters) {
            spec.filters.forEach(this._addFilter, this);
        }
    },

    _runFilters: function () {
        // make a copy of the array for comparisons
        var existingModels = slice.call(this.models);
        var rootModels = slice.call(this.collection.models);
        var offset = (this.offset || 0);
        var newModels, toAdd, toRemove;

        // reduce base model set by applying filters
        if (this._filters.length) {
            newModels = _.reduce(this._filters, function (startingArray, filterFunc) {
                return startingArray.filter(filterFunc);
            }, rootModels);
        } else {
            newModels = slice.call(rootModels);
        }

        // sort it
        if (this.comparator) newModels = _.sortBy(newModels, this.comparator);

        // trim it to length
        if (this.limit || this.offset) newModels = newModels.slice(offset, this.limit + offset);

        // now we've got our new models time to compare
        toAdd = _.difference(newModels, existingModels);
        toRemove = _.difference(existingModels, newModels);

        // save 'em
        this.models = newModels;

        _.each(toRemove, function (model) {
            this.trigger('remove', model, this);
        }, this);

        _.each(toAdd, function (model) {
            this.trigger('add', model, this);
        }, this);

        // if they contain the same models, but in new order, trigger sort
        if (!_.isEqual(existingModels, newModels)) {
            this.trigger('sort', this);
        }
    },

    _onCollectionEvent: function (eventName, model) {
        // conditions under which we should re-run filters
        if (_.contains(this._watched, eventName.split(':')[1]) || _.contains(['add', 'remove', 'reset', 'sync'], eventName)) {
            this._runFilters();
        }
        // conditions under which we should proxy the events
        if ((_.contains(['sync', 'invalid', 'destroy']) || eventName.indexOf('change') !== -1) && this.contains(model)) {
            this.trigger.apply(this, arguments);
        }
    }
});

Object.defineProperty(SubCollection.prototype, 'length', {
    get: function () {
        return this.models.length;
    }
});

Object.defineProperty(SubCollection.prototype, 'isCollection', {
    get: function () {
        return true;
    }
});

SubCollection.extend = classExtend;

module.exports = SubCollection;
<commit_msg>ensure that configure applies whole new config<commit_after>/*$AMPERSAND_VERSION*/
var _ = require('underscore');
var Events = require('backbone-events-standalone');
var classExtend = require('ampersand-class-extend');
var underscoreMixins = require('ampersand-collection-underscore-mixin');
var slice = Array.prototype.slice;

function SubCollection(collection, spec) {
    this._spec = (spec || (spec = {}));
    this.collection = collection;
    this._reset();

    this._setupComparator(spec.comparator);
    this._watch(spec.watched || []);
    this._parseFilters(spec);
    this._runFilters();
    this.listenTo(this.collection, 'all', this._onCollectionEvent);
}

_.extend(SubCollection.prototype, Events, underscoreMixins, {
    // add a filter function directly
    addFilter: function (filter) {
        this.swapFilters([filter], []);
    },

    // remove filter function directly
    removeFilter: function (filter) {
        this.swapFilters([], [filter]);
    },

    // clears filters fires events for changes
    clearFilters: function () {
        this._reset();
        this._runFilters();
    },

    // Swap out a set of old filters with a set of
    // new filters
    swapFilters: function (newFilters, oldFilters) {
        var self = this;

        if (!oldFilters) {
            oldFilters = this._filters;
        } else if (!_.isArray(oldFilters)) {
            oldFilters = [oldFilters];
        }

        if (!newFilters) {
            newFilters = [];
        } else if (!_.isArray(newFilters)) {
            newFilters = [newFilters];
        }

        oldFilters.forEach(function (filter) {
            self._removeFilter(filter);
        });

        newFilters.forEach(function (filter) {
            self._addFilter(filter);
        });

        this._runFilters();
    },

    // Update sub collection config, if `clear`
    // then clear existing filters before start.
    // This takes all the same filter arguments
    // as the init function. So you can pass:
    // {
    //   where: {
    //      name: 'something'
    //   },
    //   limit: 20
    // }
    configure: function (opts, clear) {

        if (clear) {
            this._spec = {};
            delete this.comparator;
            this._resetFilters();
        }
        _.extend(this._spec, opts);
        this._setupComparator(opts.comparator);
        if (opts.watched) this._watch(opts.watched);
        this._parseFilters(opts);
        this._runFilters();
    },

    // gets a model at a given index
    at: function (index) {
        return this.models[index];
    },

    // proxy `get` method to the underlying collection
    get: function (query, indexName) {
        var model = this.collection.get(query, indexName);
        if (model && this.contains(model)) return model;
    },

    // remove filter if found
    _removeFilter: function (filter) {
        var index = this._filters.indexOf(filter);
        if (index !== -1) {
            this._filters.splice(index, 1);
        }
    },

    // clear all filters, reset everything
    _reset: function () {
        this.models = [];
        this._resetFilters();
    },

    // just reset filters, no model changes
    _resetFilters: function () {
        this._filters = [];
        this._watched = [];
        if (this._spec.comparator) this._setupComparator(this._spec.comparator);
        if (this._spec.watched) this._watch(this._spec.watched);
        this.limit = undefined;
        this.offset = undefined;
    },

    // internal method registering new filter function
    _addFilter: function (filter) {
        this._filters.push(filter);
    },

    // adds a property or array of properties to watch, ensures uniquness.
    _watch: function (item) {
        this._watched = _.union(this._watched, _.isArray(item) ? item : [item]);
    },

    // removes a watched property
    _unwatch: function (item) {
        this._watched = _.without(this._watched, item);
    },

    // adds comparator and adds watch for it if it's a string
    _setupComparator: function (comparator) {
        if (comparator) {
            this.comparator = comparator;
            if (typeof comparator !== 'string') {
                return;
            }
            this._spec.watched = this._spec.watched || (this._spec.watched = []);
            if (this._spec.watched.indexOf(comparator) === -1) {
                this._spec.watched.push(comparator);
            }
        }
    },

    _parseFilters: function (spec) {
        if (spec.where) {
            _.each(spec.where, function (value, item) {
                this._addFilter(function (model) {
                    return (model.get ? model.get(item) : model[item]) === value;
                });
            }, this);
            // also make sure we watch all `where` keys
            this._watch(_.keys(spec.where));
        }
        if (spec.hasOwnProperty('limit')) this.limit = spec.limit;
        if (spec.hasOwnProperty('offset')) this.offset = spec.offset;
        if (spec.filter) {
            this._addFilter(spec.filter, false);
        }
        if (spec.filters) {
            spec.filters.forEach(this._addFilter, this);
        }
    },

    _runFilters: function () {
        // make a copy of the array for comparisons
        var existingModels = slice.call(this.models);
        var rootModels = slice.call(this.collection.models);
        var offset = (this.offset || 0);
        var newModels, toAdd, toRemove;

        // reduce base model set by applying filters
        if (this._filters.length) {
            newModels = _.reduce(this._filters, function (startingArray, filterFunc) {
                return startingArray.filter(filterFunc);
            }, rootModels);
        } else {
            newModels = slice.call(rootModels);
        }

        // sort it
        if (this.comparator) newModels = _.sortBy(newModels, this.comparator);

        // trim it to length
        if (this.limit || this.offset) newModels = newModels.slice(offset, this.limit + offset);

        // now we've got our new models time to compare
        toAdd = _.difference(newModels, existingModels);
        toRemove = _.difference(existingModels, newModels);

        // save 'em
        this.models = newModels;

        _.each(toRemove, function (model) {
            this.trigger('remove', model, this);
        }, this);

        _.each(toAdd, function (model) {
            this.trigger('add', model, this);
        }, this);

        // if they contain the same models, but in new order, trigger sort
        if (!_.isEqual(existingModels, newModels)) {
            this.trigger('sort', this);
        }
    },

    _onCollectionEvent: function (eventName, model) {
        // conditions under which we should re-run filters
        if (_.contains(this._watched, eventName.split(':')[1]) || _.contains(['add', 'remove', 'reset', 'sync'], eventName)) {
            this._runFilters();
        }
        // conditions under which we should proxy the events
        if ((_.contains(['sync', 'invalid', 'destroy']) || eventName.indexOf('change') !== -1) && this.contains(model)) {
            this.trigger.apply(this, arguments);
        }
    }
});

Object.defineProperty(SubCollection.prototype, 'length', {
    get: function () {
        return this.models.length;
    }
});

Object.defineProperty(SubCollection.prototype, 'isCollection', {
    get: function () {
        return true;
    }
});

SubCollection.extend = classExtend;

module.exports = SubCollection;
<|endoftext|>


<commit_before>var XML3D = require("./global.js").XML3D;
var Config = require("./interface/elements.js").config;
var sendAdapterEvent = require("./utils/misc.js").sendAdapterEvent;
var Options = require("./utils/options.js");
var CSS = require("./utils/css.js");
var ConfigureRenderer = require("./renderer/renderer/configure.js");
var WebglSupported = require("./renderer/webgl/base/utils.js").supported;
require("./interface/dom.js");
require("./utils/debug.js");

(function () {
    if (navigator.userAgent.match(/(iPad|iPhone|iPod touch)/i)) {
        var m = document.createElement("meta");
        m.name = "format-detection";
        m.content = "telephone=no";
        document.head.appendChild(m)
    }
}());

function displayWebGLNotSupportedInfo(xml3dElement){

    if(xml3dElement.hasAttribute("onunsupported")){
        var callback = new Function("event", xml3dElement.getAttribute("onunsupported"));
        xml3dElement.addEventListener('unsupported', callback, false);
    }
    var doDefault = XML3D.util.dispatchCustomEvent(xml3dElement, 'unsupported', false, true, null);
    if(doDefault){
        // Place xml3dElement inside an invisible div
        var hideDiv = document.createElementNS(XML3D.xhtmlNS, 'div');

        xml3dElement.parentNode.insertBefore(hideDiv, xml3dElement);
        hideDiv.appendChild(xml3dElement);
        hideDiv.style.display = "none";

        var infoDiv = document.createElementNS(XML3D.xhtmlNS, 'div');
        if(xml3dElement.hasAttribute("class")){
            infoDiv.setAttribute("class", xml3dElement.getAttribute("class"));
        }

        infoDiv.setAttribute("style", xml3dElement.getAttribute("style"));
        infoDiv.style.border = "2px solid red";
        infoDiv.style.fontFamily = "verdana,sans-serif";
        infoDiv.style.color = "red";
        infoDiv.style.padding = "10px";
        infoDiv.style.backgroundColor = "rgba(255, 0, 0, 0.3)";

        var width = xml3dElement.getAttribute("width");
        if (width !== null) {
            infoDiv.style.width = width;
        }

        var height = xml3dElement.getAttribute("height");
        if (height !== null) {
            infoDiv.style.height = height;
        }

        var hElement = document.createElement("h3");
        var hTxt = document.createTextNode("Sorry, your browser doesn't appear to support XML3D.");
        hElement.appendChild(hTxt);

        var pElement = document.createElement("p");
        pElement.appendChild(document.createTextNode("Please visit "));
        var link = document.createElement("a");
        link.setAttribute("href", "http://www.xml3d.org/help");
        link.appendChild(document.createTextNode("http://www.xml3d.org/help"));
        pElement.appendChild(link);
        pElement.appendChild(document.createTextNode(" for more information."));
        infoDiv.appendChild(hElement);
        infoDiv.appendChild(pElement);

        hideDiv.parentNode.insertBefore(infoDiv, hideDiv);
    }

}

/*  a list of elements that are currently initialized. More specifically,
 *  they're currently in a call to the method below.
 *
 *  Why?
 *  In webgl we actually reattach the xml3d element in the DOM. Thus, when
 *  we're in the middle of working on a onNodeInserted event, there will probably
 *  come right another event which we actually don't care for.
 *  So we use this list to keep track of which elements are currently initializing.
 */
var curXML3DInitElements = [];

/**
 * @param {Element} xml3dElement
 */
function initXML3DElement(xml3dElement) {
    if(-1 < curXML3DInitElements.indexOf(xml3dElement))
        return;

    curXML3DInitElements.push(xml3dElement);

    var debug = XML3D.debug.setup();

    if (!WebglSupported()) {
        debug && XML3D.debug.logWarning("Could not initialise WebGL, sorry :-(");
        displayWebGLNotSupportedInfo(xml3dElement);
        curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
        return;
    }

    XML3D.debug.logInfo("Configuring", xml3dElement.querySelectorAll("*").length, "elements");

    try {
        Config.configure(xml3dElement);
    } catch (e) {
        debug && XML3D.debug.logException(e);
        curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
        return;
    }
    try {
        ConfigureRenderer(xml3dElement);
    } catch (e) {
        debug && XML3D.debug.logException(e);
        curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
        return;
    }

    // initialize all attached adapters
    sendAdapterEvent(xml3dElement, {onConfigured : []});

    curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
    clearObserver();
}

/**
 * @param {Element} xml3dElement
 */
function destroyXML3DElement(xml3dElement)
{
    if(-1 < curXML3DInitElements.indexOf(xml3dElement))
        return;

    xml3dElement._configured = undefined;

    if(!xml3dElement.parentNode)
        return; // already removed

    var canvas = xml3dElement.parentNode.previousElementSibling;

    var grandParentNode = xml3dElement.parentNode.parentNode;
    if(!grandParentNode)
        return; // subtree containing canvas is not attached, can't remove it

    if(!canvas || canvas.tagName !== "canvas")
        return; // an element we didn't create, skip deletion

    grandParentNode.removeChild(xml3dElement.parentNode);
    grandParentNode.removeChild(canvas);
}

/**
 * @param {Event} evt
 */
function onNodeInserted(evt) {

    if(evt.target.tagName === "xml3d") {
        initXML3DElement(evt.target);
    }
}

/**
 * @param {Event} evt
 */
function onNodeRemoved(evt) {

    if(evt.target.tagName === "xml3d") {
        destroyXML3DElement(evt.target);
    }
}

function onLoad() {

    Options.setOptionsFromQuery();

    CSS.init();

    var debug = XML3D.debug.setup();
    debug && XML3D.debug.logInfo("xml3d.js version: " + XML3D.version);

    /**
     * Find all the XML3D tags in the document
     * @type {NodeList}
     */
    var xml3ds = document.querySelectorAll("xml3d");

    debug && XML3D.debug.logInfo("Found " + xml3ds.length + " xml3d node(s)");

    for(var i = 0; i < xml3ds.length; i++) {
        initXML3DElement(xml3ds[i]);
    }

    // TODO(ksons): Remove this, no MutationObserver no XML3D
    if(!MutationObserver){
        document.addEventListener('DOMNodeInserted', onNodeInserted, false);
        document.addEventListener('DOMNodeRemoved', onNodeRemoved, false);
    }
    else{
        observer = new MutationObserver(resolveMutations);
        observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: [ "class", "id", "style" ] } );
    }
}

function onUnload() {
    if (XML3D.document)
        XML3D.document.onunload();
}

var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver),
    observer = null;

function resolveMutations(mutations){
    for(var i = 0; i < mutations.length; ++i){
        var mutation = mutations[i];
        if(mutation.type == 'childList'){
            var addedNodes = mutation.addedNodes;
            var j = addedNodes.length;
            while(j--){
                if(addedNodes[j].tagName == "xml3d")
                    initXML3DElement(addedNodes[j]);
            }
            var removedNodes = mutation.removedNodes;
            var j = removedNodes.length;
            while(j--) {
                if(removedNodes[j].tagName == "xml3d")
                    destroyXML3DElement(removedNodes[j]);
            }

        } else if (mutation.type == 'attributes') {
            var cssTarget = mutation.target._configured ? mutation.target : mutation.target.querySelector("xml3d")
            if(cssTarget && cssTarget._configured) { // xml3d is a child node
                var adaptersNames = Object.keys(cssTarget._configured.adapters).filter(function(a) {
                    return a.indexOf("webgl") == 0;
                });
                adaptersNames.map(function(name){return cssTarget._configured.adapters[name];}).forEach(function(renderAdapter) {
                    renderAdapter.traverse(function(adapter) {
                        adapter.styleChangedCallback();
                    })

                });

            }

        }
    }
}

function flushObserver(){
    if(observer){
        resolveMutations(observer.takeRecords());
    }
}
function clearObserver(){
    if(observer){
        observer.takeRecords();
    }
}

document.addEventListener('DOMContentLoaded', onLoad, false);
window.addEventListener('unload', onUnload, false);
window.addEventListener('reload', onUnload, false);

module.exports = XML3D;

<commit_msg>if class or id changes css re-eval starts add parent<commit_after>var XML3D = require("./global.js").XML3D;
var Config = require("./interface/elements.js").config;
var sendAdapterEvent = require("./utils/misc.js").sendAdapterEvent;
var Options = require("./utils/options.js");
var CSS = require("./utils/css.js");
var ConfigureRenderer = require("./renderer/renderer/configure.js");
var WebglSupported = require("./renderer/webgl/base/utils.js").supported;
require("./interface/dom.js");
require("./utils/debug.js");

(function () {
    if (navigator.userAgent.match(/(iPad|iPhone|iPod touch)/i)) {
        var m = document.createElement("meta");
        m.name = "format-detection";
        m.content = "telephone=no";
        document.head.appendChild(m)
    }
}());

function displayWebGLNotSupportedInfo(xml3dElement){

    if(xml3dElement.hasAttribute("onunsupported")){
        var callback = new Function("event", xml3dElement.getAttribute("onunsupported"));
        xml3dElement.addEventListener('unsupported', callback, false);
    }
    var doDefault = XML3D.util.dispatchCustomEvent(xml3dElement, 'unsupported', false, true, null);
    if(doDefault){
        // Place xml3dElement inside an invisible div
        var hideDiv = document.createElementNS(XML3D.xhtmlNS, 'div');

        xml3dElement.parentNode.insertBefore(hideDiv, xml3dElement);
        hideDiv.appendChild(xml3dElement);
        hideDiv.style.display = "none";

        var infoDiv = document.createElementNS(XML3D.xhtmlNS, 'div');
        if(xml3dElement.hasAttribute("class")){
            infoDiv.setAttribute("class", xml3dElement.getAttribute("class"));
        }

        infoDiv.setAttribute("style", xml3dElement.getAttribute("style"));
        infoDiv.style.border = "2px solid red";
        infoDiv.style.fontFamily = "verdana,sans-serif";
        infoDiv.style.color = "red";
        infoDiv.style.padding = "10px";
        infoDiv.style.backgroundColor = "rgba(255, 0, 0, 0.3)";

        var width = xml3dElement.getAttribute("width");
        if (width !== null) {
            infoDiv.style.width = width;
        }

        var height = xml3dElement.getAttribute("height");
        if (height !== null) {
            infoDiv.style.height = height;
        }

        var hElement = document.createElement("h3");
        var hTxt = document.createTextNode("Sorry, your browser doesn't appear to support XML3D.");
        hElement.appendChild(hTxt);

        var pElement = document.createElement("p");
        pElement.appendChild(document.createTextNode("Please visit "));
        var link = document.createElement("a");
        link.setAttribute("href", "http://www.xml3d.org/help");
        link.appendChild(document.createTextNode("http://www.xml3d.org/help"));
        pElement.appendChild(link);
        pElement.appendChild(document.createTextNode(" for more information."));
        infoDiv.appendChild(hElement);
        infoDiv.appendChild(pElement);

        hideDiv.parentNode.insertBefore(infoDiv, hideDiv);
    }

}

/*  a list of elements that are currently initialized. More specifically,
 *  they're currently in a call to the method below.
 *
 *  Why?
 *  In webgl we actually reattach the xml3d element in the DOM. Thus, when
 *  we're in the middle of working on a onNodeInserted event, there will probably
 *  come right another event which we actually don't care for.
 *  So we use this list to keep track of which elements are currently initializing.
 */
var curXML3DInitElements = [];

/**
 * @param {Element} xml3dElement
 */
function initXML3DElement(xml3dElement) {
    if(-1 < curXML3DInitElements.indexOf(xml3dElement))
        return;

    curXML3DInitElements.push(xml3dElement);

    var debug = XML3D.debug.setup();

    if (!WebglSupported()) {
        debug && XML3D.debug.logWarning("Could not initialise WebGL, sorry :-(");
        displayWebGLNotSupportedInfo(xml3dElement);
        curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
        return;
    }

    XML3D.debug.logInfo("Configuring", xml3dElement.querySelectorAll("*").length, "elements");

    try {
        Config.configure(xml3dElement);
    } catch (e) {
        debug && XML3D.debug.logException(e);
        curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
        return;
    }
    try {
        ConfigureRenderer(xml3dElement);
    } catch (e) {
        debug && XML3D.debug.logException(e);
        curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
        return;
    }

    // initialize all attached adapters
    sendAdapterEvent(xml3dElement, {onConfigured : []});

    curXML3DInitElements.splice(curXML3DInitElements.indexOf(xml3dElement), 1);
    clearObserver();
}

/**
 * @param {Element} xml3dElement
 */
function destroyXML3DElement(xml3dElement)
{
    if(-1 < curXML3DInitElements.indexOf(xml3dElement))
        return;

    xml3dElement._configured = undefined;

    if(!xml3dElement.parentNode)
        return; // already removed

    var canvas = xml3dElement.parentNode.previousElementSibling;

    var grandParentNode = xml3dElement.parentNode.parentNode;
    if(!grandParentNode)
        return; // subtree containing canvas is not attached, can't remove it

    if(!canvas || canvas.tagName !== "canvas")
        return; // an element we didn't create, skip deletion

    grandParentNode.removeChild(xml3dElement.parentNode);
    grandParentNode.removeChild(canvas);
}

/**
 * @param {Event} evt
 */
function onNodeInserted(evt) {

    if(evt.target.tagName === "xml3d") {
        initXML3DElement(evt.target);
    }
}

/**
 * @param {Event} evt
 */
function onNodeRemoved(evt) {

    if(evt.target.tagName === "xml3d") {
        destroyXML3DElement(evt.target);
    }
}

function onLoad() {

    Options.setOptionsFromQuery();

    CSS.init();

    var debug = XML3D.debug.setup();
    debug && XML3D.debug.logInfo("xml3d.js version: " + XML3D.version);

    /**
     * Find all the XML3D tags in the document
     * @type {NodeList}
     */
    var xml3ds = document.querySelectorAll("xml3d");

    debug && XML3D.debug.logInfo("Found " + xml3ds.length + " xml3d node(s)");

    for(var i = 0; i < xml3ds.length; i++) {
        initXML3DElement(xml3ds[i]);
    }

    // TODO(ksons): Remove this, no MutationObserver no XML3D
    if(!MutationObserver){
        document.addEventListener('DOMNodeInserted', onNodeInserted, false);
        document.addEventListener('DOMNodeRemoved', onNodeRemoved, false);
    }
    else{
        observer = new MutationObserver(resolveMutations);
        observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: [ "class", "id", "style" ] } );
    }
}

function onUnload() {
    if (XML3D.document)
        XML3D.document.onunload();
}

var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver),
    observer = null;

function resolveMutations(mutations){
    for(var i = 0; i < mutations.length; ++i){
        var mutation = mutations[i];
        if(mutation.type == 'childList'){
            var addedNodes = mutation.addedNodes;
            var j = addedNodes.length;
            while(j--){
                if(addedNodes[j].tagName == "xml3d")
                    initXML3DElement(addedNodes[j]);
            }
            var removedNodes = mutation.removedNodes;
            var j = removedNodes.length;
            while(j--) {
                if(removedNodes[j].tagName == "xml3d")
                    destroyXML3DElement(removedNodes[j]);
            }

        } else if (mutation.type == 'attributes') {
            var mutationTarget = mutation.target;
            if (mutation.attributeName === "id" || mutation.attributeName === "class")
                mutationTarget = mutation.target.parentNode;
            var cssTarget = mutationTarget._configured ? mutationTarget : mutationTarget.querySelector("xml3d");
            if(cssTarget && cssTarget._configured) { // xml3d is a child node
                var adaptersNames = Object.keys(cssTarget._configured.adapters).filter(function(a) {
                    return a.indexOf("webgl") == 0;
                });
                adaptersNames.map(function(name){return cssTarget._configured.adapters[name];}).forEach(function(renderAdapter) {
                    renderAdapter.traverse(function(adapter) {
                        adapter.styleChangedCallback();
                    })

                });

            }

        }
    }
}

function flushObserver(){
    if(observer){
        resolveMutations(observer.takeRecords());
    }
}
function clearObserver(){
    if(observer){
        observer.takeRecords();
    }
}

document.addEventListener('DOMContentLoaded', onLoad, false);
window.addEventListener('unload', onUnload, false);
window.addEventListener('reload', onUnload, false);

module.exports = XML3D;

<|endoftext|>


<commit_before>+function () { "use strict";
  var scrollHandlerFn;
  var clickHandlerFn;
  var keyHandlerFn;
  var touchStartFn;
  var touchMoveFn;

  function offset(element) {
	// From http://www.quirksmode.org/js/findpos.html
	var offset = {
		top: 0,
		left: 0
	}

	if (!element.offsetParent) return offset

	do {
		offset.left += element.offsetLeft
		offset.top += element.offsetTop
	} while (element = element.offsetParent)

	return offset
  }

  /**
   * The zoom service
   */
  function ZoomService () {
    this._activeZoom            =
    this._initialScrollPosition =
    this._initialTouchPosition  =
    this._touchMoveListener     = null

    this._document = document
    this._window   = window
    this._body     = document.body
  }

  ZoomService.prototype.listen = function () {
	document.body.addEventListener('click', function (event) {
		if (event.target.dataset.action === 'zoom') this._zoom(event)
	}.bind(this))
  }

  ZoomService.prototype._zoom = function (e) {
    var target = e.target

    if (!target || target.tagName != 'IMG') return

    if (this._body.classList.contains('zoom-overlay-open')) return

    if (e.metaKey || e.ctrlKey) {
      return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank')
    }

    if (target.width >= (window.innerWidth - Zoom.OFFSET)) return

    this._activeZoomClose(true)

    this._activeZoom = new Zoom(target)
    this._activeZoom.zoomImage()

	scrollHandlerFn = this._scrollHandler.bind(this)
	clickHandlerFn = this._clickHandler.bind(this)
	keyHandlerFn = this._keyHandler.bind(this)
	touchStartFn = this._touchStart.bind(this)

    // todo(fat): probably worth throttling this
	this._window.addEventListener('scroll', scrollHandlerFn)
    this._document.addEventListener('click', clickHandlerFn)
    this._document.addEventListener('keyup', keyHandlerFn)
    this._document.addEventListener('touchstart', touchStartFn)

    e.stopPropagation()
  }

  ZoomService.prototype._activeZoomClose = function (forceDispose) {
    if (!this._activeZoom) return

    if (forceDispose) {
      this._activeZoom.dispose()
    } else {
      this._activeZoom.close()
    }

	this._window.removeEventListener('scroll', scrollHandlerFn)
    this._document.removeEventListener('click', clickHandlerFn)
    this._document.removeEventListener('keyup', keyHandlerFn)
    this._document.removeEventListener('touchstart', touchStartFn)

    this._activeZoom = null
  }

  ZoomService.prototype._scrollHandler = function (e) {
    if (this._initialScrollPosition === null) this._initialScrollPosition = window.scrollY
    var deltaY = this._initialScrollPosition - window.scrollY
    if (Math.abs(deltaY) >= 40) this._activeZoomClose()
  }

  ZoomService.prototype._keyHandler = function (e) {
    if (e.keyCode == 27) this._activeZoomClose()
  }

  ZoomService.prototype._clickHandler = function (e) {
    e.stopPropagation()
    e.preventDefault()
    this._activeZoomClose()
  }

  ZoomService.prototype._touchStart = function (e) {
    this._initialTouchPosition = e.touches[0].pageY

	touchMoveFn = this._touchMove.bind(this)
	e.target.addEventListener('touchmove', touchMoveFn)
  }

  ZoomService.prototype._touchMove = function (e) {
    if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) {
      this._activeZoomClose()
      e.target.removeEventListener('touchmove', touchMoveFn)
    }
  }

  /**
   * The zoom object
   */
  function Zoom (img) {
    this._fullHeight      =
    this._fullWidth       =
    this._overlay         =
    this._targetImageWrap = null

    this._targetImage = img

    this._body = document.body
  }

  Zoom.OFFSET = 80
  Zoom._MAX_WIDTH = 2560
  Zoom._MAX_HEIGHT = 4096

  Zoom.prototype.zoomImage = function () {
    var img = document.createElement('img')
    img.onload = function () {
      this._fullHeight = Number(img.height)
      this._fullWidth = Number(img.width)
      this._zoomOriginal()
    }.bind(this)
    img.src = this._targetImage.src
  }

  Zoom.prototype._zoomOriginal = function () {
    this._targetImageWrap           = document.createElement('div')
    this._targetImageWrap.className = 'zoom-img-wrap'

    this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage)
    this._targetImageWrap.appendChild(this._targetImage)

    this._targetImage.classList.add('zoom-img')
	this._targetImage.dataset.action = 'zoom-out'

    this._overlay           = document.createElement('div')
    this._overlay.className = 'zoom-overlay'

    document.body.appendChild(this._overlay)

    this._calculateZoom()
    this._triggerAnimation()
  }

  Zoom.prototype._calculateZoom = function () {
    this._targetImage.offsetWidth // repaint before animating

    var originalFullImageWidth  = this._fullWidth
    var originalFullImageHeight = this._fullHeight

    var scrollTop = window.scrollY

    var maxScaleFactor = originalFullImageWidth / this._targetImage.width

    var viewportHeight = (window.innerHeight - Zoom.OFFSET)
    var viewportWidth  = (window.innerWidth - Zoom.OFFSET)

    var imageAspectRatio    = originalFullImageWidth / originalFullImageHeight
    var viewportAspectRatio = viewportWidth / viewportHeight

    if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) {
      this._imgScaleFactor = maxScaleFactor

    } else if (imageAspectRatio < viewportAspectRatio) {
      this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor

    } else {
      this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor
    }
  }

  Zoom.prototype._triggerAnimation = function () {
    this._targetImage.offsetWidth // repaint before animating

	var imageOffset = offset(this._targetImage)
    var scrollTop   = window.scrollY

    var viewportY = scrollTop + (window.innerHeight / 2)
    var viewportX = (window.innerWidth / 2)

    var imageCenterY = imageOffset.top + (this._targetImage.height / 2)
    var imageCenterX = imageOffset.left + (this._targetImage.width / 2)

    this._translateY = viewportY - imageCenterY
    this._translateX = viewportX - imageCenterX

    this._targetImage.style.transform = 'scale(' + this._imgScaleFactor + ')'
    this._targetImageWrap.style.transform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px) translateZ(0)'

    this._body.classList.add('zoom-overlay-open')
  }

  Zoom.prototype.close = function () {
    this._body.classList.remove('zoom-overlay-open')
    this._body.classList.add('zoom-overlay-transitioning')

    // we use setStyle here so that the correct vender prefix for transform is used
    this._targetImage.style.transform = ''
    this._targetImageWrap.style.transform = ''

	this._targetImage.addEventListener('transitionend', this.dispose.bind(this))
  }

  Zoom.prototype.dispose = function () {
    if (this._targetImageWrap && this._targetImageWrap.parentNode) {
      this._targetImage.classList.remove('zoom-img')
      this._targetImage.dataset.action = 'zoom'

      this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap)
      this._overlay.parentNode.removeChild(this._overlay)

      this._body.classList.remove('zoom-overlay-transitioning')
    }
  }

  new ZoomService().listen()

}()
<commit_msg>add vendor transform for Safari<commit_after>+function () { "use strict";
  var scrollHandlerFn;
  var clickHandlerFn;
  var keyHandlerFn;
  var touchStartFn;
  var touchMoveFn;

  function offset(element) {
	// From http://www.quirksmode.org/js/findpos.html
	var offset = {
		top: 0,
		left: 0
	}

	if (!element.offsetParent) return offset

	do {
		offset.left += element.offsetLeft
		offset.top += element.offsetTop
	} while (element = element.offsetParent)

	return offset
  }

  /**
   * The zoom service
   */
  function ZoomService () {
    this._activeZoom            =
    this._initialScrollPosition =
    this._initialTouchPosition  =
    this._touchMoveListener     = null

    this._document = document
    this._window   = window
    this._body     = document.body
  }

  ZoomService.prototype.listen = function () {
	document.body.addEventListener('click', function (event) {
		if (event.target.dataset.action === 'zoom') this._zoom(event)
	}.bind(this))
  }

  ZoomService.prototype._zoom = function (e) {
    var target = e.target

    if (!target || target.tagName != 'IMG') return

    if (this._body.classList.contains('zoom-overlay-open')) return

    if (e.metaKey || e.ctrlKey) {
      return window.open((e.target.getAttribute('data-original') || e.target.src), '_blank')
    }

    if (target.width >= (window.innerWidth - Zoom.OFFSET)) return

    this._activeZoomClose(true)

    this._activeZoom = new Zoom(target)
    this._activeZoom.zoomImage()

	scrollHandlerFn = this._scrollHandler.bind(this)
	clickHandlerFn = this._clickHandler.bind(this)
	keyHandlerFn = this._keyHandler.bind(this)
	touchStartFn = this._touchStart.bind(this)

    // todo(fat): probably worth throttling this
	this._window.addEventListener('scroll', scrollHandlerFn)
    this._document.addEventListener('click', clickHandlerFn)
    this._document.addEventListener('keyup', keyHandlerFn)
    this._document.addEventListener('touchstart', touchStartFn)

    e.stopPropagation()
  }

  ZoomService.prototype._activeZoomClose = function (forceDispose) {
    if (!this._activeZoom) return

    if (forceDispose) {
      this._activeZoom.dispose()
    } else {
      this._activeZoom.close()
    }

	this._window.removeEventListener('scroll', scrollHandlerFn)
    this._document.removeEventListener('click', clickHandlerFn)
    this._document.removeEventListener('keyup', keyHandlerFn)
    this._document.removeEventListener('touchstart', touchStartFn)

    this._activeZoom = null
  }

  ZoomService.prototype._scrollHandler = function (e) {
    if (this._initialScrollPosition === null) this._initialScrollPosition = window.scrollY
    var deltaY = this._initialScrollPosition - window.scrollY
    if (Math.abs(deltaY) >= 40) this._activeZoomClose()
  }

  ZoomService.prototype._keyHandler = function (e) {
    if (e.keyCode == 27) this._activeZoomClose()
  }

  ZoomService.prototype._clickHandler = function (e) {
    e.stopPropagation()
    e.preventDefault()
    this._activeZoomClose()
  }

  ZoomService.prototype._touchStart = function (e) {
    this._initialTouchPosition = e.touches[0].pageY

	touchMoveFn = this._touchMove.bind(this)
	e.target.addEventListener('touchmove', touchMoveFn)
  }

  ZoomService.prototype._touchMove = function (e) {
    if (Math.abs(e.touches[0].pageY - this._initialTouchPosition) > 10) {
      this._activeZoomClose()
      e.target.removeEventListener('touchmove', touchMoveFn)
    }
  }

  /**
   * The zoom object
   */
  function Zoom (img) {
    this._fullHeight      =
    this._fullWidth       =
    this._overlay         =
    this._targetImageWrap = null

    this._targetImage = img

    this._body = document.body
  }

  Zoom.OFFSET = 80
  Zoom._MAX_WIDTH = 2560
  Zoom._MAX_HEIGHT = 4096

  Zoom.prototype.zoomImage = function () {
    var img = document.createElement('img')
    img.onload = function () {
      this._fullHeight = Number(img.height)
      this._fullWidth = Number(img.width)
      this._zoomOriginal()
    }.bind(this)
    img.src = this._targetImage.src
  }

  Zoom.prototype._zoomOriginal = function () {
    this._targetImageWrap           = document.createElement('div')
    this._targetImageWrap.className = 'zoom-img-wrap'

    this._targetImage.parentNode.insertBefore(this._targetImageWrap, this._targetImage)
    this._targetImageWrap.appendChild(this._targetImage)

    this._targetImage.classList.add('zoom-img')
	this._targetImage.dataset.action = 'zoom-out'

    this._overlay           = document.createElement('div')
    this._overlay.className = 'zoom-overlay'

    document.body.appendChild(this._overlay)

    this._calculateZoom()
    this._triggerAnimation()
  }

  Zoom.prototype._calculateZoom = function () {
    this._targetImage.offsetWidth // repaint before animating

    var originalFullImageWidth  = this._fullWidth
    var originalFullImageHeight = this._fullHeight

    var scrollTop = window.scrollY

    var maxScaleFactor = originalFullImageWidth / this._targetImage.width

    var viewportHeight = (window.innerHeight - Zoom.OFFSET)
    var viewportWidth  = (window.innerWidth - Zoom.OFFSET)

    var imageAspectRatio    = originalFullImageWidth / originalFullImageHeight
    var viewportAspectRatio = viewportWidth / viewportHeight

    if (originalFullImageWidth < viewportWidth && originalFullImageHeight < viewportHeight) {
      this._imgScaleFactor = maxScaleFactor

    } else if (imageAspectRatio < viewportAspectRatio) {
      this._imgScaleFactor = (viewportHeight / originalFullImageHeight) * maxScaleFactor

    } else {
      this._imgScaleFactor = (viewportWidth / originalFullImageWidth) * maxScaleFactor
    }
  }

  Zoom.prototype._triggerAnimation = function () {
    this._targetImage.offsetWidth // repaint before animating

	var imageOffset = offset(this._targetImage)
    var scrollTop   = window.scrollY

    var viewportY = scrollTop + (window.innerHeight / 2)
    var viewportX = (window.innerWidth / 2)

    var imageCenterY = imageOffset.top + (this._targetImage.height / 2)
    var imageCenterX = imageOffset.left + (this._targetImage.width / 2)

    this._translateY = viewportY - imageCenterY
    this._translateX = viewportX - imageCenterX

    this._targetImage.style.transform = 'scale(' + this._imgScaleFactor + ')'
    this._targetImageWrap.style.transform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px) translateZ(0)'
    this._targetImage.style.webkitTransform = 'scale(' + this._imgScaleFactor + ')'
    this._targetImageWrap.style.webkitTransform = 'translate(' + this._translateX + 'px, ' + this._translateY + 'px) translateZ(0)'

    this._body.classList.add('zoom-overlay-open')
  }

  Zoom.prototype.close = function () {
    this._body.classList.remove('zoom-overlay-open')
    this._body.classList.add('zoom-overlay-transitioning')

    // we use setStyle here so that the correct vender prefix for transform is used
    this._targetImage.style.transform = ''
    this._targetImageWrap.style.transform = ''
    this._targetImage.style.webkitTransform = ''
    this._targetImageWrap.style.webkitTransform = ''

	this._targetImage.addEventListener('transitionend', this.dispose.bind(this))
  }

  Zoom.prototype.dispose = function () {
    if (this._targetImageWrap && this._targetImageWrap.parentNode) {
      this._targetImage.classList.remove('zoom-img')
      this._targetImage.dataset.action = 'zoom'

      this._targetImageWrap.parentNode.replaceChild(this._targetImage, this._targetImageWrap)
      this._overlay.parentNode.removeChild(this._overlay)

      this._body.classList.remove('zoom-overlay-transitioning')
    }
  }

  new ZoomService().listen()

}()
<|endoftext|>


<commit_before>// At this point I can interact with the DOM as normal,
// but can't use variables or functions defined in the bootstrap script or the page itself.
// Everything here (e.g. console statements) end up the visitor's main console

console.log("extension.js is here!");

//TODO: Figure out why the bootstrap code doesn't work for gating the extension...
if ((window.location.href.toLowerCase().indexOf("frenzyboard.net") > -1) &&
    ((window.location.href.toLowerCase().indexOf("?action=browse") > -1) ||
	 (window.location.href.toLowerCase().indexOf("?action") === -1)))
{
	window.addEventListener('load', function()
	{
		console.log("extension.js onload listener");
		getTopicClassRows();
	});
}

function getTopicClassRows()
{
	var targetString = "window.status";
	var returnTrueString = "; return true;";
	// Get all TD tags with topic class
	var rows = document.querySelectorAll("td.topic");
	console.log(rows.length);
	for (var i = 0; i < rows.length; i++)
	{
		var outerHTML = rows[i].outerHTML.toLowerCase();
		// Get status text
		var statusPos = outerHTML.indexOf(targetString);
		var statusText = '';
		if (statusPos > -1)
		{
			var startPos = statusPos + targetString.length + 2;
			var endPos = outerHTML.indexOf('"', startPos);
			if (endPos == -1)
			{
				continue;
			}
			statusText = outerHTML.substring(startPos, endPos - returnTrueString.length - 1);
		}
		if (statusText != '')
		{
			var spanElement = document.createElement('span');
			// add visible=false attribute
			var attr = document.createAttribute('visible');
			attr.value = false;
			spanElement.setAttributeNode(attr);
			// add onmouseover attribute
			attr = document.createAttribute('onmouseover');
			attr.value = 'function() { log(\'' + statusText + '\'); }';
			spanElement.setAttributeNode(attr);
			spanElement.className = "bubble-item";
			rows[i].appendChild(spanElement);
		}
	}
}

function log(str)
{
	console.log(str);
}

function extractPreviewText(node, indexOfStatusStr)
{

}<commit_msg>Add yt tag support<commit_after>// At this point I can interact with the DOM as normal,
// but can't use variables or functions defined in the bootstrap script or the page itself.
// Everything here (e.g. console statements) end up the visitor's main console

console.log("extension.js is here!");

//TODO: Figure out why the bootstrap code doesn't work for gating the extension...
if (window.location.href.toLowerCase().indexOf("frenzyboard.net") > -1)
{
    if ((window.location.href.toLowerCase().indexOf("?action=browse") > -1) ||
	    (window.location.href.toLowerCase().indexOf("?action") === -1))
	{
		window.addEventListener('load', function()
		{
			console.log("extension.js load browse page listener");
			getTopicClassRows();
		});
	}

	if (window.location.href.toLowerCase().indexOf("?action=read") > -1)
	{
		window.addEventListener('load', function()
		{
			console.log("extension.js load read page listener");
			getYoutubeTags();
		});
	}
}

function getYoutubeTags()
{
	var ytTag = "[yt]";
	var endTag = "[/yt]";
	// If the board classes made some FUCKING SENSE this search
	// would be 100x simpler. But hey, take what you can get.
	var replies = document.querySelectorAll("td.window");
	console.log(replies.length);
	for (var i = 0; i < replies.length; i++)
	{
		var outerHTML = replies[i].outerHTML;
		var startTagIndex = 0;
		startTagIndex = outerHTML.indexOf(ytTag);
		var newHTML = outerHTML; // God it's so gross
		while (startTagIndex > -1)
		{
			var endTagIndex = outerHTML.indexOf(endTag, startTagIndex + ytTag.length);
			if (endTagIndex != -1)
			{
				// Grab video ID
				var fullYt = outerHTML.substring(startTagIndex, endTagIndex + endTag.length);
				// Send just the inner portion of the tags
				var ytId = getYoutubeVideoId(fullYt.substring(ytTag.length, fullYt.length - endTag.length));
				// Replace YT tag contents with YT iframe code
				var ytIframe = '<iframe width="560" height="315" src="https://www.youtube.com/embed/';
				ytIframe += ytId + '" frameborder="0" allowfullscreen></iframe>';
				// I really wish I could replace in-place but Javascript strings are immutable. FML.
				newHTML = newHTML.replace(fullYt, ytIframe);
				startTagIndex = endTagIndex + endTag.length;
			}
			else
			{
				break;
			}
			startTagIndex = outerHTML.indexOf(ytTag, startTagIndex + 1);
		}
		replies[i].outerHTML = newHTML; // Eugh
	}
}

function getYoutubeVideoId(ytStr)
{
	var slashIndex = ytStr.indexOf("/");
	var lastSlashIndex = -1;
	while (slashIndex > -1)
	{
		lastSlashIndex = slashIndex;
		slashIndex = ytStr.indexOf("/", slashIndex + 1);
	}
	if (lastSlashIndex === -1)
	{
		// If there isn't a slash at the end of the body, assume it's all an ID
		return ytStr;
	}
	else
	{
		var watchStr = "watch?v=";
		var watchIndex = ytStr.indexOf(watchStr);
		// This is likely a short-form URL (i.e. https://youtu.be/<ID>)
		if (watchIndex === -1)
		{
			return ytStr.substring(lastSlashIndex + 1);
		}
		else
		{
			// Long-form URL (i.e. https://www.youtube.com/watch?v=<ID>&<morestuff>
			var a = watchIndex + watchStr.length;
			return ytStr.substring(a, ytStr.indexOf("&", a + 1));
		}
	}
}
// This would have been used for fixing the window.status bullshit, but alas
// and alack, the old mouseover events can't be removed because they're
// anonymous functions. HOW FUCKING GREAT!
function getTopicClassRows()
{
	var targetString = "window.status";
	var returnTrueString = "; return true;";
	// Get all TD tags with topic class
	var rows = document.querySelectorAll("td.topic");
	console.log(rows.length);
	for (var i = 0; i < rows.length; i++)
	{
		var outerHTML = rows[i].outerHTML.toLowerCase();
		// Get status text
		var statusPos = outerHTML.indexOf(targetString);
		var statusText = '';
		if (statusPos > -1)
		{
			var startPos = statusPos + targetString.length + 2;
			var endPos = outerHTML.indexOf('"', startPos);
			if (endPos == -1)
			{
				continue;
			}
			statusText = outerHTML.substring(startPos, endPos - returnTrueString.length - 1);
		}
		if (statusText != '')
		{
			var spanElement = document.createElement('span');
			// add visible=false attribute
			var attr = document.createAttribute('visible');
			attr.value = false;
			spanElement.setAttributeNode(attr);
			// add onmouseover attribute
			attr = document.createAttribute('onmouseover');
			attr.value = 'function() { log(\'' + statusText + '\'); }';
			spanElement.setAttributeNode(attr);
			spanElement.className = "bubble-item";
			rows[i].appendChild(spanElement);
		}
	}
}

function log(str)
{
	console.log(str);
}

function extractPreviewText(node, indexOfStatusStr)
{

}<|endoftext|>


<commit_before>/*
 * Copyright 2019 Google LLC
 *
 * 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
 *
 *     https://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.
 */

import React, {Component} from 'react';
import {defineMessages, injectIntl} from 'react-intl';
import {withRouter} from 'react-router-dom';
import TextField, {HelperText, Input} from '@material/react-text-field';

const messages = defineMessages({
  searchForAPerson: {
    id: 'SearchBar.searchForAPerson',
    defaultMessage: 'Search for a person',
    description: 'Label on a form for searching for information about someone.',
  },
});

class SearchBar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: (props.initialValue == null ? '' : props.initialValue)};
    this.handleKeyDown = this.handleKeyDown.bind(this);
  }

  handleKeyDown(e) {
    if (e.key === 'Enter') {
      e.preventDefault();
      this.props.handleSearchFn(this.state.value);
    }
  }

  render() {
    return (
      <TextField
        label={this.props.intl.formatMessage(messages.searchForAPerson)}
        outlined
        className='searchbar'
      >
        <Input
          value={this.state.value}
          onKeyDown={this.handleKeyDown}
          onChange={(e) => this.setState({value: e.target.value})} />
      </TextField>
    );
  }
}

export default injectIntl(SearchBar);
<commit_msg>Rename handleSearchFn in SearchBar to onSearch. (#485)<commit_after>/*
 * Copyright 2019 Google LLC
 *
 * 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
 *
 *     https://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.
 */

import React, {Component} from 'react';
import {defineMessages, injectIntl} from 'react-intl';
import {withRouter} from 'react-router-dom';
import TextField, {HelperText, Input} from '@material/react-text-field';

const messages = defineMessages({
  searchForAPerson: {
    id: 'SearchBar.searchForAPerson',
    defaultMessage: 'Search for a person',
    description: 'Label on a form for searching for information about someone.',
  },
});

class SearchBar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: (props.initialValue == null ? '' : props.initialValue)};
    this.handleKeyDown = this.handleKeyDown.bind(this);
  }

  handleKeyDown(e) {
    if (e.key === 'Enter') {
      e.preventDefault();
      this.props.onSearch(this.state.value);
    }
  }

  render() {
    return (
      <TextField
        label={this.props.intl.formatMessage(messages.searchForAPerson)}
        outlined
        className='searchbar'
      >
        <Input
          value={this.state.value}
          onKeyDown={this.handleKeyDown}
          onChange={(e) => this.setState({value: e.target.value})} />
      </TextField>
    );
  }
}

export default injectIntl(SearchBar);
<|endoftext|>


<commit_before>var GraphPkg = {};
(function() {
	////////////////////
	//edge definition //
	////////////////////

	GraphPkg.jsEdge = function(edgeKey, edgeWeight) {
		this.origin = null;
		this.destination = null;
		this.edgeKey = edgeKey;
		this.edgeWeight = edgeWeight;
		this.visited = false;
	};

	GraphPkg.jsEdge.prototype.getOpposite = function(vert) {
		if (vert === this.destination) {
			return this.origin;
		} else {
			return this.destination;
		}
	}

	GraphPkg.jsEdge.prototype.getOrigin = function() {
		return this.origin;
	}

	GraphPkg.jsEdge.prototype.setOrigin = function(origin) {
		this.origin = origin;
	}

	GraphPkg.jsEdge.prototype.getDestination = function() {
		return this.destination;
	}

	GraphPkg.jsEdge.prototype.setDestination = function(destination) {
		this.destination = destination;
	}

	GraphPkg.jsEdge.prototype.getKey = function() {
		return this.edgeKey;
	}

	GraphPkg.jsEdge.prototype.setKey = function(data) {
		this.edgeKey = data;
	}

	GraphPkg.jsEdge.prototype.getWeight = function() {
		return this.edgeWeight;
	}

	GraphPkg.jsEdge.prototype.setWeight = function(data) {
		this.edgeWeight = data;
	}

	GraphPkg.jsEdge.prototype.visited = function() {
		return this.visited;
	}

	GraphPkg.jsEdge.prototype.setVisited = function(bool) {
		this.visited = bool;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//vertex definition. ... Spinning, cycling, channeling closer and closer together, all mass converging into a single point, my life, circling around a transparent sphere, a flat circle, a captain, taking his ship across the Atlantic, only to find his home on the other side, a captain, taking his boat across the Atlantic, only to give up and turn around three-quarters of the way there, a captain, taking his canoe across the marsh of dead trees, finding a woman there, tied to the branch of a weeping willow, she asks what he is doing there, and he says he is stuck in a vortex, oh, well, i suppose we all are, aren't we, a captain, talking to his wife in bed, was it thirteen years ago, before he left to Atlantic.  //
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	GraphPkg.jsVertex = function(vertKey, vertData) {

		this.vertKey = vertKey;
		this.vertData = vertData;
		this.incidentEdges = {};
		this.outgoingEdges = {};
		this.incidentEdgeCounter = 0;
		this.outgoingEdgeCounter = 0;

	};

	GraphPkg.jsVertex.prototype.getKey = function() {
		return this.vertKey;
	}

	GraphPkg.jsVertex.prototype.getData = function() {
		return this.vertData;
	}

	GraphPkg.jsVertex.prototype.setData = function(newData) {
		this.vertKey = newData;
	}

	GraphPkg.jsVertex.prototype.getIncidentEdges = function() {
		return this.incidentEdges;
	}

	GraphPkg.jsVertex.prototype.insertIncidentEdge = function(edge) {
		this.incidentEdges[edge.getKey().toString()] = edge;
		this.incidentEdgeCounter++;
	}

	GraphPkg.jsVertex.prototype.removeIncidentEdge = function(edge) {
		delete this.incidentEdges[edge.getKey().toString()];
		this.incidentEdgeCounter--;
	}

	GraphPkg.jsVertex.prototype.clearIncidentEdges = function() {
		this.incidentEdges = {};
		this.incidentEdgeCounter = 0;
	}

	GraphPkg.jsVertex.prototype.getNumberOfIncidentEdges = function() {
		return this.incidentEdgeCounter;
	}

	GraphPkg.jsVertex.prototype.getOutgoingEdges = function() {
		return this.outgoingEdges;
	}

	GraphPkg.jsVertex.prototype.insertOutgoingEdge = function(edge) {
		this.outgoingEdges[edge.getKey().toString()] = edge;
		this.outgoingEdgeCounter++;
	}

	GraphPkg.jsVertex.prototype.removeOutgoingEdge = function(edge) {
		delete this.outgoingEdges[edge.getKey().toString()];
		this.outgoingEdgeCounter--;
	}

	GraphPkg.jsVertex.prototype.clearOutgoingEdges = function() {
		this.outgoingEdges = {};
		this.outgoingEdgeCounter = 0;
	}

	GraphPkg.jsVertex.prototype.getNumberOfOutgoingEdges = function() {
		return this.outgoingEdgeCounter;
	}

	//////////////////////
	// graph contructor //
	/////////////////////

	GraphPkg.jsGraph = function() {

		this.graphVerts = {};
		this.graphEdges = {};
	};

	GraphPkg.jsGraph.prototype.vertices = function() {
		return this.graphVerts;
	};

	GraphPkg.jsGraph.prototype.getVertex = function(key) {
		return this.graphVerts[key.toString()];
	};

	GraphPkg.jsGraph.prototype.getEdge = function(key) {
		return this.graphEdges[key.toString()];
	};

	GraphPkg.jsGraph.prototype.edges = function() {
		return this.graphEdges;
	};

	GraphPkg.jsGraph.prototype.incidentEdges = function(vertex) {
		return vertex.getIncidentEdges();
	};

	GraphPkg.jsGraph.prototype.outgoingEdges = function(vertex) {
		return vertex.getOutgoingEdges();
	};

	GraphPkg.jsGraph.prototype.getNumberOfIncidentEdges = function(vertex) {
		return vertex.getNumberOfIncidentEdges();
	}

	GraphPkg.jsGraph.prototype.getNumberOfOutgoingEdges = function(vertex) {
		return vertex.getNumberOfOutgoingEdges();
	}
	//todo
	GraphPkg.jsGraph.prototype.areAdjacent = function(v1, v2) {
		return true;
	};

	GraphPkg.jsGraph.prototype.endVertices = function(edge) {
		var endVerts = [];
		endVerts.push(edge.getOrigin());
		endVerts.push(edge.getDestination());
		return endVerts;
	};

	GraphPkg.jsGraph.prototype.opposite = function(vertex, edge) {
		if (edge.getOrigin.getKey() === vertex.getKey()) {
			return edge.getDestination();
		} else {
			return edge.getOrigin();
		}
	};

	GraphPkg.jsGraph.prototype.insertVertex = function(vertex) {
		this.graphVerts[vertex.getKey().toString()] = vertex;
	};

	GraphPkg.jsGraph.prototype.insertEdge = function(v1, v2, edge) {
		edge.setOrigin(v1);
		edge.setDestination(v2);
		v1.insertOutgoingEdge(edge);
		v2.insertIncidentEdge(edge);
		this.graphEdges[edge.getKey().toString()] = edge;
	};

	GraphPkg.jsGraph.prototype.insertUndirectedEdge = function(v1, v2, edge) {
		edge.setOrigin(v1);
		edge.setDestination(v2);
		v1.insertOutgoingEdge(edge);
		v1.insertIncidentEdge(edge);

		v2.insertIncidentEdge(edge);
		v2.insertOutgoingEdge(edge);
		this.graphEdges[edge.getKey().toString()] = edge;
	};

	//todo need to update jsGraph's edges table
	GraphPkg.jsGraph.prototype.removeVertex = function(vertex) {
		vertex.clearIncidentEdges();
		vertex.clearOutgoingEdges();
		delete this.graphVerts[vertex.getKey().toString()];
	};

	GraphPkg.jsGraph.prototype.removeEdge = function(edge) {
		edge.getOrigin().removeOutgoingEdge(edge);
		edge.getDestination().removeIncidentEdge(edge);
		delete this.graphEdges[edge.getKey().toString()];
	};

	GraphPkg.jsGraph.prototype.removeUndirectedEdge = function(edge) {
		edge.getOrigin().removeOutgoingEdge(edge);
		edge.getOrigin().removeIncidentEdge(edge);
		edge.getDestination().removeOutgoingEdge(edge);
		edge.getDestination().removeIncidentEdge(edge);
		delete this.graphEdges[edge.getKey().toString()];
	};

}());<commit_msg>added edge type id and wrote remove vertex method<commit_after>var GraphPkg = {};
(function() {
	////////////////////
	//edge definition //
	////////////////////

	GraphPkg.jsEdge = function(edgeKey, edgeWeight) {
		this.edgeType = 'undirected',
		this.origin = null;
		this.destination = null;
		this.edgeKey = edgeKey;
		this.edgeWeight = edgeWeight;
		this.visited = false;
	};

	GraphPkg.jsEdge.prototype.getOpposite = function(vert) {
		if (vert === this.destination) {
			return this.origin;
		} else {
			return this.destination;
		}
	}

	GraphPkg.jsEdge.prototype.getOrigin = function() {
		return this.origin;
	}

	GraphPkg.jsEdge.prototype.setOrigin = function(origin) {
		this.origin = origin;
	}

	GraphPkg.jsEdge.prototype.getDestination = function() {
		return this.destination;
	}

	GraphPkg.jsEdge.prototype.setDestination = function(destination) {
		this.destination = destination;
	}

	GraphPkg.jsEdge.prototype.getKey = function() {
		return this.edgeKey;
	}

	GraphPkg.jsEdge.prototype.setKey = function(data) {
		this.edgeKey = data;
	}

	GraphPkg.jsEdge.prototype.getWeight = function() {
		return this.edgeWeight;
	}

	GraphPkg.jsEdge.prototype.setWeight = function(data) {
		this.edgeWeight = data;
	}

	GraphPkg.jsEdge.prototype.visited = function() {
		return this.visited;
	}

	GraphPkg.jsEdge.prototype.setVisited = function(bool) {
		this.visited = bool;
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//vertex definition. ... Spinning, cycling, channeling closer and closer together, all mass converging into a single point, my life, circling around a transparent sphere, a flat circle, a captain, taking his ship across the Atlantic, only to find his home on the other side, a captain, taking his boat across the Atlantic, only to give up and turn around three-quarters of the way there, a captain, taking his canoe across the marsh of dead trees, finding a woman there, tied to the branch of a weeping willow, she asks what he is doing there, and he says he is stuck in a vortex, oh, well, i suppose we all are, aren't we, a captain, talking to his wife in bed, was it thirteen years ago, before he left to Atlantic.  //
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	GraphPkg.jsVertex = function(vertKey, vertData) {

		this.vertKey = vertKey;
		this.vertData = vertData;
		this.incidentEdges = {};
		this.outgoingEdges = {};
		this.incidentEdgeCounter = 0;
		this.outgoingEdgeCounter = 0;

	};

	GraphPkg.jsVertex.prototype.getKey = function() {
		return this.vertKey;
	}

	GraphPkg.jsVertex.prototype.getData = function() {
		return this.vertData;
	}

	GraphPkg.jsVertex.prototype.setData = function(newData) {
		this.vertKey = newData;
	}

	GraphPkg.jsVertex.prototype.getIncidentEdges = function() {
		return this.incidentEdges;
	}

	GraphPkg.jsVertex.prototype.insertIncidentEdge = function(edge) {
		this.incidentEdges[edge.getKey().toString()] = edge;
		this.incidentEdgeCounter++;
	}

	GraphPkg.jsVertex.prototype.removeIncidentEdge = function(edge) {
		delete this.incidentEdges[edge.getKey().toString()];
		this.incidentEdgeCounter--;
	}

	GraphPkg.jsVertex.prototype.clearIncidentEdges = function() {
		var tempEdges = this.incidentEdges;
		this.incidentEdges = {};
		this.incidentEdgeCounter = 0;
		return tempEdges;
	}

	GraphPkg.jsVertex.prototype.getNumberOfIncidentEdges = function() {
		return this.incidentEdgeCounter;
	}

	GraphPkg.jsVertex.prototype.getOutgoingEdges = function() {
		return this.outgoingEdges;
	}

	GraphPkg.jsVertex.prototype.insertOutgoingEdge = function(edge) {
		this.outgoingEdges[edge.getKey().toString()] = edge;
		this.outgoingEdgeCounter++;
	}

	GraphPkg.jsVertex.prototype.removeOutgoingEdge = function(edge) {
		delete this.outgoingEdges[edge.getKey().toString()];
		this.outgoingEdgeCounter--;
	}

	GraphPkg.jsVertex.prototype.clearOutgoingEdges = function() {
		var tempEdges = this.outgoingEdges;
		this.outgoingEdges = {};
		this.outgoingEdgeCounter = 0;
		return tempEdges;
	}

	GraphPkg.jsVertex.prototype.getNumberOfOutgoingEdges = function() {
		return this.outgoingEdgeCounter;
	}

	//////////////////////
	// graph contructor //
	/////////////////////

	GraphPkg.jsGraph = function() {

		this.graphVerts = {};
		this.graphEdges = {};
	};

	GraphPkg.jsGraph.prototype.vertices = function() {
		return this.graphVerts;
	};

	GraphPkg.jsGraph.prototype.getVertex = function(key) {
		return this.graphVerts[key.toString()];
	};

	GraphPkg.jsGraph.prototype.getEdge = function(key) {
		return this.graphEdges[key.toString()];
	};

	GraphPkg.jsGraph.prototype.edges = function() {
		return this.graphEdges;
	};

	GraphPkg.jsGraph.prototype.incidentEdges = function(vertex) {
		return vertex.getIncidentEdges();
	};

	GraphPkg.jsGraph.prototype.outgoingEdges = function(vertex) {
		return vertex.getOutgoingEdges();
	};

	GraphPkg.jsGraph.prototype.getNumberOfIncidentEdges = function(vertex) {
		return vertex.getNumberOfIncidentEdges();
	}

	GraphPkg.jsGraph.prototype.getNumberOfOutgoingEdges = function(vertex) {
		return vertex.getNumberOfOutgoingEdges();
	}
	//todo
	GraphPkg.jsGraph.prototype.areAdjacent = function(v1, v2) {
		return true;
	};

	GraphPkg.jsGraph.prototype.endVertices = function(edge) {
		var endVerts = [];
		endVerts.push(edge.getOrigin());
		endVerts.push(edge.getDestination());
		return endVerts;
	};

	GraphPkg.jsGraph.prototype.opposite = function(vertex, edge) {
		if (edge.getOrigin.getKey() === vertex.getKey()) {
			return edge.getDestination();
		} else {
			return edge.getOrigin();
		}
	};

	GraphPkg.jsGraph.prototype.insertVertex = function(vertex) {
		this.graphVerts[vertex.getKey().toString()] = vertex;
	};

	GraphPkg.jsGraph.prototype.insertEdge = function(v1, v2, edge) {
		edge.edgeType = 'directed';
		edge.setOrigin(v1);
		edge.setDestination(v2);
		v1.insertOutgoingEdge(edge);
		v2.insertIncidentEdge(edge);
		this.graphEdges[edge.getKey().toString()] = edge;
	};

	GraphPkg.jsGraph.prototype.insertUndirectedEdge = function(v1, v2, edge) {
		edge.edgeType = 'undirected';
		edge.setOrigin(v1);
		edge.setDestination(v2);
		v1.insertOutgoingEdge(edge);
		v1.insertIncidentEdge(edge);

		v2.insertIncidentEdge(edge);
		v2.insertOutgoingEdge(edge);
		this.graphEdges[edge.getKey().toString()] = edge;
	};

	GraphPkg.jsGraph.prototype.removeVertex = function(vertex) {
		var inEdges = vertex.clearIncidentEdges();
		for (var edge in inEdges) {
			if (inEdges.hasOwnProperty(edge)) {
				if (inEdges[edge].edgeType === 'directed') {
					this.removeEdge(inEdges[edge]);
				} else {
					this.removeUndirectedEdge(inEdges[edge]);
				}
			}
		}
		var outEdges = vertex.clearOutgoingEdges();
		for (var edge in outEdges) {
			if (outEdges.hasOwnProperty(edge)) {
				if (outEdges[edge].edgeType === 'directed') {
					this.removeEdge(outEdges[edge]);
				} else {
					this.removeUndirectedEdge(outEdges[edge]);
				}
			}
		}
		delete this.graphVerts[vertex.getKey().toString()];
	};

	GraphPkg.jsGraph.prototype.removeEdge = function(edge) {
		edge.getOrigin().removeOutgoingEdge(edge);
		edge.getDestination().removeIncidentEdge(edge);
		delete this.graphEdges[edge.getKey().toString()];
	};

	GraphPkg.jsGraph.prototype.removeUndirectedEdge = function(edge) {
		edge.getOrigin().removeOutgoingEdge(edge);
		edge.getOrigin().removeIncidentEdge(edge);
		edge.getDestination().removeOutgoingEdge(edge);
		edge.getDestination().removeIncidentEdge(edge);
		delete this.graphEdges[edge.getKey().toString()];
	};

}());<|endoftext|>


<commit_before>var vscode = require('vscode');

function activate(context) {

    var disposable = vscode.commands.registerCommand('cdnjs.cdnjs', function() {

        var window = vscode.window;

        console.log('test');

    });

    context.subscriptions.push(disposable);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {}
exports.deactivate = deactivate;<commit_msg>Added initial search functionality that returns results into QuickPickBox<commit_after>var vscode = require('vscode');
var request = require('request');

function activate(context) {

    // cdnjs.com api endpoint
    var url = 'https://api.cdnjs.com/libraries';

    var disposable = vscode.commands.registerCommand('cdnjs.cdnjs', function() {

        var window = vscode.window;

        // Show search box
        window.showInputBox({
            'placeHolder': 'Search for a library, for example: jquery'
        }).then(function(value) {

            // Search the cdnjs api
            request(url + '?search=' + value, function(err, res, body) {

                // TODO: Need to add error handling here
                // for err, res.status != 200 and !body.results

                var results = JSON.parse(body).results;
                var pickItems = [];

                for (var index in results) {
                    if (results.hasOwnProperty(index)) {
                        var element = results[index];
                        if (element.name) {
                            pickItems.push(element.name);
                        }
                    }
                }

                window.showQuickPick(pickItems, {
                        'placeHolder': "Results found: " + results.length
                    })
                    .then(function(value) {
                        console.log(value);
                    });

            });
        });

    });

    context.subscriptions.push(disposable);
}
exports.activate = activate;

// this method is called when your extension is deactivated
function deactivate() {}
exports.deactivate = deactivate;<|endoftext|>


<commit_before>/*
 * Copyright © 2012 Will Thompson <will@willthompson.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

const Lang = imports.lang;
const Main = imports.ui.main;

const Tp = imports.gi.TelepathyGLib;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Panel = imports.ui.panel;

/* The values are string->bool dicts rather than arrays because I don't know
 * offhand how to check if a string is in an array.
 */
const Accounts = {
    "Me": {
        "salut/local_2dxmpp/account0": true,
        "gabble/jabber/will_40willthompson_2eco_2euk0": true
    },
    "Collabora": {
        "gabble/jabber/will_2ethompson_40collabora_2eco_2euk0": true,
        "idle/irc/wjt0": true
    },
    "SIP": {
        "sofiasip/sip/_31002739_40sipgate_2eco_2euk0": true,
        "sofiasip/sip/will_2ethompson_40voip_2ecollabora_2eco_2euk0": true
    },
    "Musicians": {
        "gabble/jabber/t_2dpain_40test_2ecollabora_2eco_2euk0": true,
        "gabble/jabber/lady_2dgaga_40test_2ecollabora_2eco_2euk0": true
    },
    "WE ARE SEX BOB-OMB!\nONE TWO THREE FOUR": {
        "gabble/jabber/scott_40sp_2elit0": true,
        "gabble/jabber/ramona_40sp_2elit0": true
    },
};

function CAGMenu() {
    this._init();
}

CAGMenu.prototype = {
    __proto__: PanelMenu.SystemStatusButton.prototype,

    _init: function() {
        PanelMenu.SystemStatusButton.prototype._init.call(this, 'avatar-default-symbolic', null);
        this._widgets = {};
        this._accounts = {};

        let accountNames = Object.keys(Accounts).sort();

        for (var group in Accounts) {
            let widget = new PopupMenu.PopupSwitchMenuItem(group, true);
            widget.setStatus("loading\u2026");
            this.menu.addMenuItem(widget);
            this._widgets[group] = widget;
            this._accounts[group] = [];
        }

        this._am = Tp.AccountManager.dup();
        this._am.prepare_async(null, Lang.bind(this,
            function(am) {
                let accounts = am.get_valid_accounts();

                for (let group in Accounts) {
                    let widget = this._widgets[group];
                    let state = false;

                    for (let i = 0; i < accounts.length; i++) {
                        let account = accounts[i];
                        if (account.get_path_suffix() in Accounts[group]) {
                            this._accounts[group].push(account);
                            /* If any of our work-y accounts are enabled, show the
                             * whole lot as enabled.
                             */
                            state = state || account.is_enabled();
                        }
                    }

                    widget.setToggleState(state);
                    widget.setStatus(null);

                    /* If we don't do this stupid dance, all callbacks get the
                     * final group name. I hate how binding works in JavaScript
                     * and Python.
                     */
                    let groupAgain = group;
                    widget.connect('toggled', Lang.bind(this,
                        function(item) { this._toggled(groupAgain, item); }));
                }
            }));
    },

    _toggled: function(group, item) {
        let state = item.state;

        for (let i = 0; i < this._accounts[group].length; i++) {
            let account = this._accounts[group][i];
            account.set_enabled_async(state, Lang.bind(this, function() {
                if (state) {
                    let [presence, status, msg] = this._am.get_most_available_presence();

                    account.request_presence_async(presence, status, msg,
                        function() {});
                }
            }));
        }
    }
};

function init(metadata) {
}

let groupsMenu;

function enable() {
    groupsMenu = new CAGMenu;
    Main.panel.addToStatusArea('chat-account-groups-menu', groupsMenu);
}

function disable() {
    groupsMenu.destroy();
}
<commit_msg>Refactor some stuff into a class<commit_after>/*
 * Copyright © 2012 Will Thompson <will@willthompson.co.uk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

const Lang = imports.lang;
const Main = imports.ui.main;

const Tp = imports.gi.TelepathyGLib;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Panel = imports.ui.panel;

/* The values are string->bool dicts rather than arrays because I don't know
 * offhand how to check if a string is in an array.
 */
const Accounts = {
    "Me": {
        "salut/local_2dxmpp/account0": true,
        "gabble/jabber/will_40willthompson_2eco_2euk0": true
    },
    "Collabora": {
        "gabble/jabber/will_2ethompson_40collabora_2eco_2euk0": true,
        "idle/irc/wjt0": true
    },
    "SIP": {
        "sofiasip/sip/_31002739_40sipgate_2eco_2euk0": true,
        "sofiasip/sip/will_2ethompson_40voip_2ecollabora_2eco_2euk0": true
    },
    "Musicians": {
        "gabble/jabber/t_2dpain_40test_2ecollabora_2eco_2euk0": true,
        "gabble/jabber/lady_2dgaga_40test_2ecollabora_2eco_2euk0": true
    },
    "WE ARE SEX BOB-OMB!\nONE TWO THREE FOUR": {
        "gabble/jabber/scott_40sp_2elit0": true,
        "gabble/jabber/ramona_40sp_2elit0": true
    },
};

function AccountGroupSection() {
    this._init.apply(this, arguments);
}

AccountGroupSection.prototype = {
    __proto__: PopupMenu.PopupMenuSection.prototype,

    _init: function(am, groupName, accountIDs) {
        PopupMenu.PopupMenuSection.prototype._init.call(this);

        this._am = am;

        this._switch = new PopupMenu.PopupSwitchMenuItem(groupName, true);
        this._switch.setStatus("loading\u2026");
        this.addMenuItem(this._switch);

        this._accountIDs = accountIDs;
        this._accounts = [];
    },

    helloThere: function(accounts) {
        let state = false;

        for (let i = 0; i < accounts.length; i++) {
            let account = accounts[i];
            if (account.get_path_suffix() in this._accountIDs) {
                this._accounts.push(account);
                /* If any account in this group is enabled, show the whole
                 * group as enabled.
                 */
                state = state || account.is_enabled();
            }
        }

        this._switch.setToggleState(state);
        this._switch.setStatus(null);

        this._switch.connect('toggled', Lang.bind(this,
            function(item) { this._onToggled(item); }));
    },

    _onToggled: function(item) {
        let state = item.state;

        for (let i = 0; i < this._accounts.length; i++) {
            let account = this._accounts[i];
            account.set_enabled_async(state, Lang.bind(this, function() {
                if (state) {
                    let [presence, status, msg] = this._am.get_most_available_presence();

                    account.request_presence_async(presence, status, msg,
                        function() {});
                }
            }));
        }

    },
}

function CAGMenu() {
    this._init();
}

CAGMenu.prototype = {
    __proto__: PanelMenu.SystemStatusButton.prototype,

    _init: function() {
        PanelMenu.SystemStatusButton.prototype._init.call(this, 'avatar-default-symbolic', null);

        let am = Tp.AccountManager.dup();
        let sections = [];

        for (var groupName in Accounts) {
            let section = new AccountGroupSection(am, groupName, Accounts[groupName]);
            this.menu.addMenuItem(section);
            sections.push(section);
        }

        am.prepare_async(null, Lang.bind(this,
            function(am) {
                let accounts = am.get_valid_accounts();

                for (let i = 0; i < sections.length; i++) {
                    sections[i].helloThere(accounts);
                }
            }));
    },
};

function init(metadata) {
}

let groupsMenu;

function enable() {
    groupsMenu = new CAGMenu;
    Main.panel.addToStatusArea('chat-account-groups-menu', groupsMenu);
}

function disable() {
    groupsMenu.destroy();
}
<|endoftext|>