<commit_before>var utils = {};

(function () {
    utils.forEach = function (object, callback) {
        var i = 0,
            keys = Object.keys(object),
            length = keys.length;
        for (; i < length; i++) {
            if (callback(keys[i], object[keys[i]]) === false) {
                return;
            }
        }
    };

    utils.extend = function (target) {
        var mergeCallback = function (key, value) { this[key] = value; };
        for (var i = 1; i < arguments.length; i++) {
            utils.forEach(arguments[i], mergeCallback.bind(target));
        }
        return target;
    };

    utils.isPlainObject = function (object) {
        if ((object !== null) && (typeof object === 'object')) {
            if (typeof Object.getPrototypeOf === 'function') {
                var objectPrototype = Object.getPrototypeOf(object);
                return ((objectPrototype === Object.prototype) || (objectPrototype === null));
            }

            return (Object.prototype.toString.call(object) === '[object Object]');
        }

        return false;
    };

    utils.createClass = function (constructor, baseClass, blueprint) {
        if (blueprint === undefined) {
            blueprint = baseClass;
            baseClass = undefined;
        }
        if (baseClass) {
            constructor.prototype = Object.create(baseClass.prototype);
            constructor.prototype.constructor = constructor;
        }
        if (blueprint) {
            var parent = baseClass ? baseClass.prototype : undefined;
            blueprint.call(constructor, parent);
        }
        return constructor;
    };

    utils.ajax = function (url, options) {
        var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
        xhr.open(options.postData ? 'POST' : 'GET', url);

        if (options.responseType) {
            xhr.responseType = options.responseType;
            if (options.responseType === 'json') xhr.setRequestHeader('Accept', 'application/json');
        }

        if (options.header) {
            utils.forEach(options.header, function (header, value) {
                xhr.setRequestHeader(header, value);
            });
        }

        xhr.onreadystatechange = function() {
            if (xhr.readyState > 3) {
                var isSuccessful = null;
                if ((xhr.status >= 200) && (xhr.status < 300)) {
                    isSuccessful = true;
                } else if ((xhr.status === 0) || ((xhr.status >= 400) && (xhr.status <= 600))) {
                    isSuccessful = false;
                } else {
                    if (options.complete) {
                        options.complete(xhr, xhr.statusText, xhr.response, null);
                    }

                    console.error('Invalid XMLHttpRequest status', xhr.status);
                    return;
                }

                if (isSuccessful && options.success) {
                    var callbackResult = options.success(xhr, xhr.statusText, xhr.response);
                    isSuccessful = (callbackResult || (callbackResult === undefined) || (callbackResult === null));
                }
                if (!isSuccessful && options.error) {
                    options.error(xhr, xhr.statusText, xhr.response);
                }

                if (options.complete) {
                    options.complete(xhr, xhr.statusText, xhr.response, isSuccessful);
                }
            }
        };

        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        if (options.postData) {
            var postParams = Object.keys(options.postData).map(function (key) {
                return encodeURIComponent(key) + '=' + encodeURIComponent(options.postData[key]);
            }).join('&');

            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('Content-Length', options.postData.length);

            xhr.send(postParams);
        } else {
            xhr.send();
        }

        return xhr;
    };

    utils.slideUp = function (element, finishCallback, startCallback) {
        utils.slideOut(element, {
            cssRule: 'height',
            cssRuleRef: 'clientHeight',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideDown = function (element, finishCallback, startCallback) {
        utils.slideIn(element, {
            cssRule: 'height',
            cssRuleRef: 'clientHeight',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideLeft = function (element, finishCallback, startCallback) {
        utils.slideOut(element, {
            cssRule: 'width',
            cssRuleRef: 'clientWidth',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideRight = function (element, finishCallback, startCallback) {
        utils.slideIn(element, {
            cssRule: 'width',
            cssRuleRef: 'clientWidth',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideOut = function (element, options) {
        element.style[options.cssRule] = element[options.cssRuleRef] + 'px';

        var slideId = parseInt(element.dataset.slideId) || 0;
        element.dataset.slideId = ++slideId;

        window.requestAnimationFrame(function () {
            element.classList.add('slide');

            window.requestAnimationFrame(function () {
                element.style[options.cssRule] = '0px';

                if (options.startCallback) {
                    options.startCallback();
                }

                window.setTimeout(function () {
                    if (parseInt(element.dataset.slideId) !== slideId) return;

                    element.classList.add('hidden');
                    element.classList.remove('slide');
                    element.style[options.cssRule] = null;

                    if (options.finishCallback) {
                        window.requestAnimationFrame(options.finishCallback);
                    }
                }, 500);
            });
        });
    };

    utils.slideIn = function (element, options) {
        var cssRuleOriginalValue = element[options.cssRuleRef] + 'px',
            slideId = parseInt(element.dataset.slideId) || 0;

        element.dataset.slideId = ++slideId;

        element.style[options.cssRule] = null;
        element.classList.remove('hidden');
        element.classList.remove('slide');
        var cssRuleValue = element[options.cssRuleRef] + 'px';

        element.style[options.cssRule] = cssRuleOriginalValue;

        window.requestAnimationFrame(function () {
            element.classList.add('slide');

            window.requestAnimationFrame(function () {
                element.style[options.cssRule] = cssRuleValue;

                if (options.startCallback) {
                    options.startCallback();
                }

                window.setTimeout(function () {
                    if (parseInt(element.dataset.slideId) !== slideId) return;

                    element.classList.remove('slide');
                    element.style[options.cssRule] = null;

                    if (options.finishCallback) {
                        window.requestAnimationFrame(options.finishCallback);
                    }
                }, 500);
            });
        });
    };

    /**
     * Create a HTML element from a string
     *
     * based on jQuery.buildFragment()
     * https://github.com/jquery/jquery/blob/1.11.3/src/manipulation.js
     *
     * jQuery JavaScript Library v1.11.3
     * http://jquery.com/
     *
     * Copyright 2005, 2015 jQuery Foundation and other contributors
     * Released under the MIT license
     * http://jquery.org/license
     */
    utils.parse = function (html) {
        var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
            rtagName = /<([\w:]+)/,
            rhtml = /<|&#?\w+;/,
            wrapMap = {
                option: [ 1, "<select multiple='multiple'>", "</select>" ],
                legend: [ 1, "<fieldset>", "</fieldset>" ],
                area: [ 1, "<map>", "</map>" ],
                param: [ 1, "<object>", "</object>" ],
                thead: [ 1, "
", "
" ],
                tr: [ 2, "

", "

" ],
                col: [ 2, "

<colgroup>", "</colgroup>
" ],
                td: [ 3, "

", "

" ],
                _default: [ 0, "", "" ]
            },
            nodes = [];
        wrapMap.optgroup = wrapMap.option, wrapMap.th = wrapMap.td,
        wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;

        if (!rhtml.test(html)) {
            // convert non-html into a text node
            return document.createTextNode(html);
        } else {
            // convert html into DOM nodes
            var tmp = document.createElement('div');

            // deserialize a standard representation
            var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(),
                wrap = wrapMap[tag] || wrapMap._default;

            tmp.innerHTML = wrap[1] + html.replace(rxhtmlTag, "<$1></$2>" ) + wrap[2];

            // descend through wrappers to the right content
            var j = wrap[0];
            while (j--) tmp = tmp.lastChild;

            return (tmp.childNodes.length) > 1 ? tmp : tmp.lastChild;
        }
    };

    utils.matches = function (element, selector) {
        if (element.matches) {
            return element.matches(selector);
        } else if (element.webkitMatchesSelector) {
            return element.webkitMatchesSelector(selector);
        } else if (element.msMatchesSelector) {
            return element.msMatchesSelector(selector);
        } else {
            console.error('Unable to call utils.matches(…): Not supported');
            return false;
        }
    };

    utils.closest = function (element, selector) {
        if (element.closest) {
            return element.closest(selector);
        }

        do {
            if (utils.matches(element, selector)) {
                return element;
            }
        } while ((element = element.parentElement) !== null);

        return null;
    };
})();
<commit_msg>JavaScript: Add utils.encodeUriParams() and use it for utils.ajax()<commit_after>var utils = {};

(function () {
    utils.forEach = function (object, callback) {
        var i = 0,
            keys = Object.keys(object),
            length = keys.length;
        for (; i < length; i++) {
            if (callback(keys[i], object[keys[i]]) === false) {
                return;
            }
        }
    };

    utils.extend = function (target) {
        var mergeCallback = function (key, value) { this[key] = value; };
        for (var i = 1; i < arguments.length; i++) {
            utils.forEach(arguments[i], mergeCallback.bind(target));
        }
        return target;
    };

    utils.isPlainObject = function (object) {
        if ((object !== null) && (typeof object === 'object')) {
            if (typeof Object.getPrototypeOf === 'function') {
                var objectPrototype = Object.getPrototypeOf(object);
                return ((objectPrototype === Object.prototype) || (objectPrototype === null));
            }

            return (Object.prototype.toString.call(object) === '[object Object]');
        }

        return false;
    };

    utils.createClass = function (constructor, baseClass, blueprint) {
        if (blueprint === undefined) {
            blueprint = baseClass;
            baseClass = undefined;
        }
        if (baseClass) {
            constructor.prototype = Object.create(baseClass.prototype);
            constructor.prototype.constructor = constructor;
        }
        if (blueprint) {
            var parent = baseClass ? baseClass.prototype : undefined;
            blueprint.call(constructor, parent);
        }
        return constructor;
    };

    utils.encodeUriParams = function(params) {
        var iterator = function (params, keyPrefix) {
            var result = '';
            utils.forEach(params, function (key, value) {
                key = encodeURIComponent(key);
                key = keyPrefix ? (keyPrefix + "[" + key + "]") : key;

                if (Array.isArray(value) || utils.isPlainObject(value)) {
                    result += iterator(value, key);
                } else {
                    result += "&" + key + "=" + encodeURIComponent(value);
                }
            });
            return result;
        };

        var result = iterator(params);
        return (params !== '') ? result.slice(1) : '';
    };

    utils.ajax = function (url, options) {
        if (options.queryParams) {
            var queryString = utils.encodeUriParams(options.queryParams);
            if (queryString !== '') url += (url.indexOf('?') === -1) ? '?' + queryString : '&' + queryString;
        }

        var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
        xhr.open(options.postData ? 'POST' : 'GET', url);

        if (options.responseType) {
            xhr.responseType = options.responseType;
            if (options.responseType === 'json') xhr.setRequestHeader('Accept', 'application/json');
        }

        if (options.header) {
            utils.forEach(options.header, function (header, value) {
                xhr.setRequestHeader(header, value);
            });
        }

        xhr.onreadystatechange = function() {
            if (xhr.readyState > 3) {
                var isSuccessful = null;
                if ((xhr.status >= 200) && (xhr.status < 300)) {
                    isSuccessful = true;
                } else if ((xhr.status === 0) || ((xhr.status >= 400) && (xhr.status <= 600))) {
                    isSuccessful = false;
                } else {
                    if (options.complete) {
                        options.complete(xhr, xhr.statusText, xhr.response, null);
                    }

                    console.error('Invalid XMLHttpRequest status', xhr.status);
                    return;
                }

                if (isSuccessful && options.success) {
                    var callbackResult = options.success(xhr, xhr.statusText, xhr.response);
                    isSuccessful = (callbackResult || (callbackResult === undefined) || (callbackResult === null));
                }
                if (!isSuccessful && options.error) {
                    options.error(xhr, xhr.statusText, xhr.response);
                }

                if (options.complete) {
                    options.complete(xhr, xhr.statusText, xhr.response, isSuccessful);
                }
            }
        };

        xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
        if (options.postData) {
            var postParams = utils.encodeUriParams(options.postData);

            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('Content-Length', options.postData.length);

            xhr.send(postParams);
        } else {
            xhr.send();
        }

        return xhr;
    };

    utils.slideUp = function (element, finishCallback, startCallback) {
        utils.slideOut(element, {
            cssRule: 'height',
            cssRuleRef: 'clientHeight',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideDown = function (element, finishCallback, startCallback) {
        utils.slideIn(element, {
            cssRule: 'height',
            cssRuleRef: 'clientHeight',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideLeft = function (element, finishCallback, startCallback) {
        utils.slideOut(element, {
            cssRule: 'width',
            cssRuleRef: 'clientWidth',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideRight = function (element, finishCallback, startCallback) {
        utils.slideIn(element, {
            cssRule: 'width',
            cssRuleRef: 'clientWidth',
            startCallback: startCallback,
            finishCallback: finishCallback
        });
    };

    utils.slideOut = function (element, options) {
        element.style[options.cssRule] = element[options.cssRuleRef] + 'px';

        var slideId = parseInt(element.dataset.slideId) || 0;
        element.dataset.slideId = ++slideId;

        window.requestAnimationFrame(function () {
            element.classList.add('slide');

            window.requestAnimationFrame(function () {
                element.style[options.cssRule] = '0px';

                if (options.startCallback) {
                    options.startCallback();
                }

                window.setTimeout(function () {
                    if (parseInt(element.dataset.slideId) !== slideId) return;

                    element.classList.add('hidden');
                    element.classList.remove('slide');
                    element.style[options.cssRule] = null;

                    if (options.finishCallback) {
                        window.requestAnimationFrame(options.finishCallback);
                    }
                }, 500);
            });
        });
    };

    utils.slideIn = function (element, options) {
        var cssRuleOriginalValue = element[options.cssRuleRef] + 'px',
            slideId = parseInt(element.dataset.slideId) || 0;

        element.dataset.slideId = ++slideId;

        element.style[options.cssRule] = null;
        element.classList.remove('hidden');
        element.classList.remove('slide');
        var cssRuleValue = element[options.cssRuleRef] + 'px';

        element.style[options.cssRule] = cssRuleOriginalValue;

        window.requestAnimationFrame(function () {
            element.classList.add('slide');

            window.requestAnimationFrame(function () {
                element.style[options.cssRule] = cssRuleValue;

                if (options.startCallback) {
                    options.startCallback();
                }

                window.setTimeout(function () {
                    if (parseInt(element.dataset.slideId) !== slideId) return;

                    element.classList.remove('slide');
                    element.style[options.cssRule] = null;

                    if (options.finishCallback) {
                        window.requestAnimationFrame(options.finishCallback);
                    }
                }, 500);
            });
        });
    };

    /**
     * Create a HTML element from a string
     *
     * based on jQuery.buildFragment()
     * https://github.com/jquery/jquery/blob/1.11.3/src/manipulation.js
     *
     * jQuery JavaScript Library v1.11.3
     * http://jquery.com/
     *
     * Copyright 2005, 2015 jQuery Foundation and other contributors
     * Released under the MIT license
     * http://jquery.org/license
     */
    utils.parse = function (html) {
        var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
            rtagName = /<([\w:]+)/,
            rhtml = /<|&#?\w+;/,
            wrapMap = {
                option: [ 1, "<select multiple='multiple'>", "</select>" ],
                legend: [ 1, "<fieldset>", "</fieldset>" ],
                area: [ 1, "<map>", "</map>" ],
                param: [ 1, "<object>", "</object>" ],
                thead: [ 1, "
", "
" ],
                tr: [ 2, "

", "

" ],
                col: [ 2, "

<colgroup>", "</colgroup>
" ],
                td: [ 3, "

", "

" ],
                _default: [ 0, "", "" ]
            },
            nodes = [];
        wrapMap.optgroup = wrapMap.option, wrapMap.th = wrapMap.td,
        wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;

        if (!rhtml.test(html)) {
            // convert non-html into a text node
            return document.createTextNode(html);
        } else {
            // convert html into DOM nodes
            var tmp = document.createElement('div');

            // deserialize a standard representation
            var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(),
                wrap = wrapMap[tag] || wrapMap._default;

            tmp.innerHTML = wrap[1] + html.replace(rxhtmlTag, "<$1></$2>" ) + wrap[2];

            // descend through wrappers to the right content
            var j = wrap[0];
            while (j--) tmp = tmp.lastChild;

            return (tmp.childNodes.length) > 1 ? tmp : tmp.lastChild;
        }
    };

    utils.matches = function (element, selector) {
        if (element.matches) {
            return element.matches(selector);
        } else if (element.webkitMatchesSelector) {
            return element.webkitMatchesSelector(selector);
        } else if (element.msMatchesSelector) {
            return element.msMatchesSelector(selector);
        } else {
            console.error('Unable to call utils.matches(…): Not supported');
            return false;
        }
    };

    utils.closest = function (element, selector) {
        if (element.closest) {
            return element.closest(selector);
        }

        do {
            if (utils.matches(element, selector)) {
                return element;
            }
        } while ((element = element.parentElement) !== null);

        return null;
    };
})();
<|endoftext|>


<commit_before>var stargazerApp = angular.module('stargazerApp', ['ngSanitize']);

stargazerApp.controller('stargazerController', function ($scope, $sce, stargazerFactory) {
	marked.setOptions({
	  renderer: new marked.Renderer(),
	  gfm: true,
	  tables: true,
	  breaks: false,
	  pedantic: false,
	  sanitize: true,
	  smartLists: true,
	  smartypants: false
	});

	$scope.tags = [];
	$scope.languages = [];
	$scope.sorts = [];
	$scope.untagged = 0;
	$scope.selected = {};
	$scope.searchText = '';
	$scope.sortSelected = 'title';
	$scope.repos = [];
	$scope.curRepo = {'readme': marked("# Welcome. \r\n Let's show you how to use stargazer.")};
	filters = ['general', 'repo', 'tag', 'language'];

	init();
	function init() {
		for (i = 0; i < filters.length; ++i) {
			$scope.selected[filters[i]] = [];
		}
		stargazerFactory.importFromPouchDB(function () {
			$scope.$apply(function () {
				$scope.tags = stargazerFactory.getTags();
				$scope.languages = stargazerFactory.getLanguages().sort();
				$scope.sorts = stargazerFactory.getSorts();
				$scope.untagged = stargazerFactory.getUntagged();
				$scope.repos = stargazerFactory.getRepos();
				for (i = 0; i < $scope.repos.length; ++i) {
					$scope.repos[i].visible = true;
				}
			});
		});
	}

	function setReadme(index) {
		$.ajax({
			url: $scope.repos[index].readme,
			dataType: "json",
			success: function (data) {
				$scope.$apply(function () {
					result = marked(decodeURIComponent(escape(atob(data.content.replace(/\n/g, "")))));
					$scope.curRepo.readme = result;
				});
			}
		});
	}

	$scope.select = function (filter, item) {
		index = $scope.selected[filter].indexOf(item);
		if (index > -1) {
			$scope.selected[filter].splice(index, 1);
		} else {
			$scope.selected[filter].push(item);
		}

		if (filter == 'repo') {
			$scope.selected[filter] = [item];
			for (i = 0; i < $scope.repos.length; ++i) {
				if ($scope.repos[i].title == item) {
					setReadme(i);
					break;
				}
			}
		} else if (filter == 'tag') {
			for (i = 0; i < $scope.repos.length; ++i) {
				$scope.repos[i].visible = true;
				for (j = 0; j < $scope.selected[filter].length; ++j) {
					if ($scope.repos[i].tags.indexOf($scope.selected.tag[j]) <= -1) {
						$scope.repos[i].visible = false;
					}
				}
			}
		} else if (filter == 'language') {
			for (i = 0; i < $scope.repos.length; ++i) {
				$scope.repos[i].visible = true;
				if ($scope.selected[filter].length > 0 && $scope.selected[filter].indexOf($scope.repos[i].language) <= -1) {
					$scope.repos[i].visible = false;
				}
			}
		}
	};

	$scope.isActive = function (filter, item) {
		return $scope.selected[filter].indexOf(item) > -1;
	};

	$scope.isHidden = function (title) {
		for (i = 0; i < $scope.repos.length; ++i) {
			if ($scope.repos[i].title === title) {
				return !$scope.repos[i].visible;
			}
		}
	};

	$scope.selectSort = function (item) {
		if ($scope.sortSelected === item.toLowerCase()) {
			$scope.sortSelected = '-' + item.toLowerCase();
		}
		else {
			$scope.sortSelected = item.toLowerCase();
		}
	};

	curTitle = null;

	$scope.tokenfieldClicked = function (title) {
		if (title === curTitle) {
			return;
		}
		curTitle = title;

		console.log(title);
		$('#' + title + ' .tag').hide();

		local = [];
		for (i = 0; i < $scope.tags.length; i++) {
			local.push({value: $scope.tags[i]});
		}

		var engine = new Bloodhound({
			local: local,
			datumTokenizer: function(d) {
				return Bloodhound.tokenizers.whitespace(d.value);
			},
			queryTokenizer: Bloodhound.tokenizers.whitespace
		});
		engine.initialize();
		$('#' + title + ' #tokenfield').tokenfield({
			typeahead: [null, { source: engine.ttAdapter() }]
		});

		for (i = 0; i < $scope.repos.length; i++) {
			repo = $scope.repos[i];
			if (repo.title === title) {
				break;
			}
		}

		for (i = 0; i < repo.tags.length; i++) {
			$('#' + title + ' #tokenfield').tokenfield('createToken', repo.tags[i]);
		}

		$('#' + title + ' #tokenfield').show();
		$('#' + title + ' #tokenfield').on('tokenfield:createdtoken', function (event) {
			var existingTokens = $(this).tokenfield('getTokens');
			$scope.$apply(function () {
				repo.tags.push(existingTokens[existingTokens.length - 1].label);
			});
			$('#' + title + ' .tag').show();
			$('#' + title + ' #tokenfield').tokenfield('destroy');
			$('#' + title + ' #tokenfield').hide();
		});
	};

	// $(document).ready(function () {
	// 	console.log($(".description"));
	// 	$(".description").ellipsis({
	// 		row: 2
	// 	});
	// });
});
<commit_msg>delete tags.<commit_after>var stargazerApp = angular.module('stargazerApp', ['ngSanitize']);

stargazerApp.controller('stargazerController', function ($scope, $sce, stargazerFactory) {
	marked.setOptions({
	  renderer: new marked.Renderer(),
	  gfm: true,
	  tables: true,
	  breaks: false,
	  pedantic: false,
	  sanitize: true,
	  smartLists: true,
	  smartypants: false
	});

	$scope.tags = [];
	$scope.languages = [];
	$scope.sorts = [];
	$scope.untagged = 0;
	$scope.selected = {};
	$scope.searchText = '';
	$scope.sortSelected = 'title';
	$scope.repos = [];
	$scope.curRepo = {'readme': marked("# Welcome. \r\n Let's show you how to use stargazer.")};
	filters = ['general', 'repo', 'tag', 'language'];

	init();
	function init() {
		for (i = 0; i < filters.length; ++i) {
			$scope.selected[filters[i]] = [];
		}
		stargazerFactory.importFromPouchDB(function () {
			$scope.$apply(function () {
				$scope.tags = stargazerFactory.getTags();
				$scope.languages = stargazerFactory.getLanguages().sort();
				$scope.sorts = stargazerFactory.getSorts();
				$scope.untagged = stargazerFactory.getUntagged();
				$scope.repos = stargazerFactory.getRepos();
				for (i = 0; i < $scope.repos.length; ++i) {
					$scope.repos[i].visible = true;
				}
			});
		});
	}

	function setReadme(index) {
		$.ajax({
			url: $scope.repos[index].readme,
			dataType: "json",
			success: function (data) {
				$scope.$apply(function () {
					result = marked(decodeURIComponent(escape(atob(data.content.replace(/\n/g, "")))));
					$scope.curRepo.readme = result;
				});
			}
		});
	}

	$scope.select = function (filter, item) {
		index = $scope.selected[filter].indexOf(item);
		if (index > -1) {
			$scope.selected[filter].splice(index, 1);
		} else {
			$scope.selected[filter].push(item);
		}

		if (filter == 'repo') {
			$scope.selected[filter] = [item];
			for (i = 0; i < $scope.repos.length; ++i) {
				if ($scope.repos[i].title == item) {
					setReadme(i);
					break;
				}
			}
		} else if (filter == 'tag') {
			for (i = 0; i < $scope.repos.length; ++i) {
				$scope.repos[i].visible = true;
				for (j = 0; j < $scope.selected[filter].length; ++j) {
					if ($scope.repos[i].tags.indexOf($scope.selected.tag[j]) <= -1) {
						$scope.repos[i].visible = false;
					}
				}
			}
		} else if (filter == 'language') {
			for (i = 0; i < $scope.repos.length; ++i) {
				$scope.repos[i].visible = true;
				if ($scope.selected[filter].length > 0 && $scope.selected[filter].indexOf($scope.repos[i].language) <= -1) {
					$scope.repos[i].visible = false;
				}
			}
		}
	};

	$scope.isActive = function (filter, item) {
		return $scope.selected[filter].indexOf(item) > -1;
	};

	$scope.isHidden = function (title) {
		for (i = 0; i < $scope.repos.length; ++i) {
			if ($scope.repos[i].title === title) {
				return !$scope.repos[i].visible;
			}
		}
	};

	$scope.selectSort = function (item) {
		if ($scope.sortSelected === item.toLowerCase()) {
			$scope.sortSelected = '-' + item.toLowerCase();
		}
		else {
			$scope.sortSelected = item.toLowerCase();
		}
	};

	curTitle = null;

	$scope.tokenfieldClicked = function (title) {
		if (title === curTitle) {
			return;
		}
		curTitle = title;

		console.log(title);
		$('#' + title + ' .tag').hide();

		local = [];
		for (i = 0; i < $scope.tags.length; i++) {
			local.push({value: $scope.tags[i]});
		}

		var engine = new Bloodhound({
			local: local,
			datumTokenizer: function(d) {
				return Bloodhound.tokenizers.whitespace(d.value);
			},
			queryTokenizer: Bloodhound.tokenizers.whitespace
		});
		engine.initialize();
		$('#' + title + ' #tokenfield').tokenfield({
			typeahead: [null, { source: engine.ttAdapter() }]
		});

		for (i = 0; i < $scope.repos.length; i++) {
			repo = $scope.repos[i];
			if (repo.title === title) {
				break;
			}
		}

		for (i = 0; i < repo.tags.length; i++) {
			$('#' + title + ' #tokenfield').tokenfield('createToken', repo.tags[i]);
		}

		$('#' + title + ' #tokenfield').show();
		$('#' + title + ' #tokenfield').on('tokenfield:createdtoken', function (event) {
			var existingTokens = $(this).tokenfield('getTokens');
			$scope.$apply(function () {
				repo.tags.push(existingTokens[existingTokens.length - 1].label);
			});
			$('#' + title + ' .tag').show();
			$('#' + title + ' #tokenfield').tokenfield('destroy');
			$('#' + title + ' #tokenfield').hide();
		});
		$('#' + title + ' #tokenfield').on('tokenfield:removedtoken', function (event) {
			var existingTokens = $(this).tokenfield('getTokens');
			$scope.$apply(function () {
				tags = [];
				$.each(existingTokens, function(index, token) {
					tags.push(token.label);
			    });
				repo.tags = tags;
			});
			$('#' + title + ' .tag').show();
			$('#' + title + ' #tokenfield').tokenfield('destroy');
			$('#' + title + ' #tokenfield').hide();
		});
	};

	// $(document).ready(function () {
	// 	console.log($(".description"));
	// 	$(".description").ellipsis({
	// 		row: 2
	// 	});
	// });
});
<|endoftext|>


<commit_before>define([
	'underscore',
	'backbone',
	'text!templates/atom.tpl',
	'vent'
], function(_, Backbone, Template, vent) {
	return Backbone.Marionette.ItemView.extend({
		template: _.template(Template),

		templateHelpers: function() {
			return {
				option_selected: function(boolean_statement) {
					return boolean_statement ? "selected" : "";
				},
				item_list_selection: this.hasItemListSelection(),

				quantity_visible: this.isQuantityVisible(),
				content_visible:  this.isContentVisible (),
				location_visible: this.isLocationVisible(),

				content_items:          this.isContentItems(),
				content_tags:           this.isContentTags(),
				content_plaques:        this.isContentPlaques(),
				content_dialogs:        this.isContentDialogs(),
				content_dialog_scripts: this.isContentDialogScripts(),
				content_web_pages:      this.isContentWebPages(),
				content_quests:         this.isContentQuests(),
				content_web_hooks:      this.isContentWebHooks(),

				items: this.items,
				tags: this.tags,
				plaques: this.plaques,
				dialogs: this.dialogs,
				dialog_scripts: this.dialog_scripts,
				web_pages: this.web_pages,
				quests: this.quests,
				web_hooks: this.web_hooks
			};
		},

		// Bootstrap
		tagName: 'li',
		className: "list-group-item",

		initialize: function(options) {
			this.items = options.contents.items;
			this.tags = options.contents.tags;
			this.plaques = options.contents.plaques;
			this.dialogs = options.contents.dialogs;
			this.dialog_scripts = options.contents.dialog_scripts;
			this.web_pages = options.contents.web_pages;
			this.quests = options.contents.quests;
			this.web_hooks = options.contents.web_hooks;
			this.notes = options.contents.notes;
		},

		ui: {
			operator:    ".boolean-operator",
			requirement: ".requirement",
			content:     ".content",
			quantity:    ".quantity",
			latitude:    ".latitude",
			longitude:   ".longitude",
			distance:    ".distance"
		},

		events: {
			"change @ui.operator":    "onChangeBooleanOperator",
			"change @ui.requirement": "onChangeRequirement",
			"change @ui.content":     "onChangeContent",
			"change @ui.quantity":    "onChangeQuantity",
			"change @ui.distance":    "onChangeDistance",
			"change @ui.latitude":    "onChangeLatitude",
			"change @ui.longitude":   "onChangeLongitude",
			"click .delete-atom":     "onClickDeleteAtom"
		},

		/* Model bindings */

		onChangeBooleanOperator: function() {
			var value = this.ui.operator.find("option:selected").val();
			var type  = this.ui.operator.find("option:selected").data("set");

			// Change requirement to one from proper dropdown
			if(type === "item_list")
			{
				// Switch to value from first list.
				if(!this.hasItemListSelection())
				{
					this.model.set("requirement", "PLAYER_HAS_ITEM");
					this.model.set("content_id", "0");
				}

			}
			else {
				// Switch to value from second list.
				if(this.hasItemListSelection())
				{
					this.model.set("requirement", "PLAYER_VIEWED_DIALOG");
					this.model.set("content_id", "0");
				}
			}

			this.model.set("bool_operator", value);

			this.render();
		},

		onChangeRequirement: function() {
			var value = this.ui.requirement.find("option:selected").val();
			this.model.set("requirement", value);

			// 0 out content ID before re-rendering select
			this.model.set("content_id", "0");
			this.render();
		},

		onChangeContent: function() {
			var value = this.ui.content.find("option:selected").val();
			this.model.set("content_id", value);
		},

		onChangeQuantity:  function() { this.model.set("qty",       this.ui.quantity.val ()); },
		onChangeLatitude:  function() { this.model.set("latitude",  this.ui.latitude.val ()); },
		onChangeLongitude: function() { this.model.set("longitude", this.ui.longitude.val()); },
		onChangeDistance:  function() { this.model.set("distance",  this.ui.distance.val ()); },

		onClickDeleteAtom: function() {
			this.trigger("atom:remove", this.model);
		},

		/* Visibility Logic */

		hasItemListSelection: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_HAS_TAGGED_ITEM":
					return true;
				default:
					return false;
			}
		},

		isQuantityVisible: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_HAS_TAGGED_ITEM":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_IMAGE":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_AUDIO":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_VIDEO":
				case "PLAYER_HAS_NOTE":
				case "PLAYER_HAS_NOTE_WITH_TAG":
				case "PLAYER_HAS_NOTE_WITH_LIKES":
				case "PLAYER_HAS_NOTE_WITH_COMMENTS":
				case "PLAYER_HAS_GIVEN_NOTE_COMMENTS":
					return true;

				default:
					return false;
			}
		},

		isContentVisible: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_HAS_TAGGED_ITEM":
				case "PLAYER_VIEWED_ITEM":
				case "PLAYER_VIEWED_PLAQUE":
				case "PLAYER_VIEWED_DIALOG":
				case "PLAYER_VIEWED_DIALOG_SCRIPT":
				case "PLAYER_VIEWED_WEB_PAGE":
				case "PLAYER_HAS_COMPLETED_QUEST":
				case "PLAYER_HAS_RECEIVED_ICOMING_WEB_HOOK":
				case "PLAYER_HAS_NOTE_WITH_TAG":
					return true;

				default:
					return false;
			}
		},

		isLocationVisible: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_IMAGE":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_AUDIO":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_VIDEO":
					return true;

				default:
					return false;
			}
		},

		/* Content lists visibility logic */
		isContentItems: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_VIEWED_ITEM":
					return true;

				default:
					return false;
			}
		},

		isContentTags: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_TAGGED_ITEM":
				case "PLAYER_HAS_NOTE_WITH_TAG":
					return true;

				default:
					return false;
			}
		},

		isContentPlaques: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_PLAQUE":
					return true;

				default:
					return false;
			}
		},

		isContentDialogs: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_DIALOG":
					return true;

				default:
					return false;
			}
		},

		isContentDialogScripts: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_DIALOG_SCRIPT":
					return true;

				default:
					return false;
			}
		},

		isContentWebPages: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_WEB_PAGE":
					return true;

				default:
					return false;
			}
		},

		isContentQuests: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_COMPLETED_QUEST":
					return true;

				default:
					return false;
			}
		},

		isContentWebHooks: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_RECEIVED_ICOMING_WEB_HOOK":
					return true;

				default:
					return false;
			}
		}
	});
});

<commit_msg>Unused collection<commit_after>define([
	'underscore',
	'backbone',
	'text!templates/atom.tpl',
	'vent'
], function(_, Backbone, Template, vent) {
	return Backbone.Marionette.ItemView.extend({
		template: _.template(Template),

		templateHelpers: function() {
			return {
				option_selected: function(boolean_statement) {
					return boolean_statement ? "selected" : "";
				},
				item_list_selection: this.hasItemListSelection(),

				quantity_visible: this.isQuantityVisible(),
				content_visible:  this.isContentVisible (),
				location_visible: this.isLocationVisible(),

				content_items:          this.isContentItems(),
				content_tags:           this.isContentTags(),
				content_plaques:        this.isContentPlaques(),
				content_dialogs:        this.isContentDialogs(),
				content_dialog_scripts: this.isContentDialogScripts(),
				content_web_pages:      this.isContentWebPages(),
				content_quests:         this.isContentQuests(),
				content_web_hooks:      this.isContentWebHooks(),

				items: this.items,
				tags: this.tags,
				plaques: this.plaques,
				dialogs: this.dialogs,
				dialog_scripts: this.dialog_scripts,
				web_pages: this.web_pages,
				quests: this.quests,
				web_hooks: this.web_hooks
			};
		},

		// Bootstrap
		tagName: 'li',
		className: "list-group-item",

		initialize: function(options) {
			this.items = options.contents.items;
			this.tags = options.contents.tags;
			this.plaques = options.contents.plaques;
			this.dialogs = options.contents.dialogs;
			this.dialog_scripts = options.contents.dialog_scripts;
			this.web_pages = options.contents.web_pages;
			this.quests = options.contents.quests;
			this.web_hooks = options.contents.web_hooks;
		},

		ui: {
			operator:    ".boolean-operator",
			requirement: ".requirement",
			content:     ".content",
			quantity:    ".quantity",
			latitude:    ".latitude",
			longitude:   ".longitude",
			distance:    ".distance"
		},

		events: {
			"change @ui.operator":    "onChangeBooleanOperator",
			"change @ui.requirement": "onChangeRequirement",
			"change @ui.content":     "onChangeContent",
			"change @ui.quantity":    "onChangeQuantity",
			"change @ui.distance":    "onChangeDistance",
			"change @ui.latitude":    "onChangeLatitude",
			"change @ui.longitude":   "onChangeLongitude",
			"click .delete-atom":     "onClickDeleteAtom"
		},

		/* Model bindings */

		onChangeBooleanOperator: function() {
			var value = this.ui.operator.find("option:selected").val();
			var type  = this.ui.operator.find("option:selected").data("set");

			// Change requirement to one from proper dropdown
			if(type === "item_list")
			{
				// Switch to value from first list.
				if(!this.hasItemListSelection())
				{
					this.model.set("requirement", "PLAYER_HAS_ITEM");
					this.model.set("content_id", "0");
				}

			}
			else {
				// Switch to value from second list.
				if(this.hasItemListSelection())
				{
					this.model.set("requirement", "PLAYER_VIEWED_DIALOG");
					this.model.set("content_id", "0");
				}
			}

			this.model.set("bool_operator", value);

			this.render();
		},

		onChangeRequirement: function() {
			var value = this.ui.requirement.find("option:selected").val();
			this.model.set("requirement", value);

			// 0 out content ID before re-rendering select
			this.model.set("content_id", "0");
			this.render();
		},

		onChangeContent: function() {
			var value = this.ui.content.find("option:selected").val();
			this.model.set("content_id", value);
		},

		onChangeQuantity:  function() { this.model.set("qty",       this.ui.quantity.val ()); },
		onChangeLatitude:  function() { this.model.set("latitude",  this.ui.latitude.val ()); },
		onChangeLongitude: function() { this.model.set("longitude", this.ui.longitude.val()); },
		onChangeDistance:  function() { this.model.set("distance",  this.ui.distance.val ()); },

		onClickDeleteAtom: function() {
			this.trigger("atom:remove", this.model);
		},

		/* Visibility Logic */

		hasItemListSelection: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_HAS_TAGGED_ITEM":
					return true;
				default:
					return false;
			}
		},

		isQuantityVisible: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_HAS_TAGGED_ITEM":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_IMAGE":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_AUDIO":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_VIDEO":
				case "PLAYER_HAS_NOTE":
				case "PLAYER_HAS_NOTE_WITH_TAG":
				case "PLAYER_HAS_NOTE_WITH_LIKES":
				case "PLAYER_HAS_NOTE_WITH_COMMENTS":
				case "PLAYER_HAS_GIVEN_NOTE_COMMENTS":
					return true;

				default:
					return false;
			}
		},

		isContentVisible: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_HAS_TAGGED_ITEM":
				case "PLAYER_VIEWED_ITEM":
				case "PLAYER_VIEWED_PLAQUE":
				case "PLAYER_VIEWED_DIALOG":
				case "PLAYER_VIEWED_DIALOG_SCRIPT":
				case "PLAYER_VIEWED_WEB_PAGE":
				case "PLAYER_HAS_COMPLETED_QUEST":
				case "PLAYER_HAS_RECEIVED_ICOMING_WEB_HOOK":
				case "PLAYER_HAS_NOTE_WITH_TAG":
					return true;

				default:
					return false;
			}
		},

		isLocationVisible: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_IMAGE":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_AUDIO":
				case "PLAYER_HAS_UPLOADED_MEDIA_ITEM_VIDEO":
					return true;

				default:
					return false;
			}
		},

		/* Content lists visibility logic */
		isContentItems: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_ITEM":
				case "PLAYER_VIEWED_ITEM":
					return true;

				default:
					return false;
			}
		},

		isContentTags: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_TAGGED_ITEM":
				case "PLAYER_HAS_NOTE_WITH_TAG":
					return true;

				default:
					return false;
			}
		},

		isContentPlaques: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_PLAQUE":
					return true;

				default:
					return false;
			}
		},

		isContentDialogs: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_DIALOG":
					return true;

				default:
					return false;
			}
		},

		isContentDialogScripts: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_DIALOG_SCRIPT":
					return true;

				default:
					return false;
			}
		},

		isContentWebPages: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_VIEWED_WEB_PAGE":
					return true;

				default:
					return false;
			}
		},

		isContentQuests: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_COMPLETED_QUEST":
					return true;

				default:
					return false;
			}
		},

		isContentWebHooks: function() {
			switch(this.model.get("requirement")) {
				case "PLAYER_HAS_RECEIVED_ICOMING_WEB_HOOK":
					return true;

				default:
					return false;
			}
		}
	});
});

<|endoftext|>


<commit_before>define(['sprd/model/ProductBase', 'js/core/List', 'sprd/data/ConfigurationTypeResolver', 'sprd/entity/DesignConfiguration', 'sprd/entity/TextConfiguration', 'sprd/entity/SpecialTextConfiguration', 'sprd/entity/Price', 'js/data/TypeResolver', 'js/data/Entity', "underscore", "flow", "sprd/manager/IProductManager", "sprd/error/ProductCreationError", 'sprd/model/ProductType', 'sprd/entity/Appearance', 'sprd/entity/BendingTextConfiguration'],
    function (ProductBase, List, ConfigurationTypeResolver, DesignConfiguration, TextConfiguration, SpecialTextConfiguration, Price, TypeResolver, Entity, _, flow, IProductManager, ProductCreationError, ProductType, Appearance, BendingTextConfiguration) {

        var undefined;

        return ProductBase.inherit("sprd.model.Product", {

            schema: {
                productType: ProductType,
                configurations: [new ConfigurationTypeResolver({
                    mapping: {
                        "design": DesignConfiguration,
                        "text": TextConfiguration,
                        "specialText": SpecialTextConfiguration,
                        "bendingText": BendingTextConfiguration
                    }
                })],
                appearance: {
                    type: Appearance,
                    isReference: true
                },
                restrictions: Object,
                defaultValues: Object,
                price: {
                    type: Price,
                    generated: true
                },
                creator: String,
                templateId: {
                    type: String,
                    required: false
                }
            },

            defaults: {
                configurations: List,
                creator: null
            },

            inject: {
                manager: IProductManager
            },

            ctor: function () {
                this.configurationsOnViewCache = {};

                this.callBase();

                var priceChangeHandler = function () {
                    this.trigger("priceChanged");
                };

                var productChangeHandler = function () {
                    this.trigger("productChanged");
                };

                var configurationAdd = function (e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._setUpConfiguration(configuration);

                    if (configuration instanceof TextConfiguration) {
                        configuration._composeText();
                    }
                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);

                };

                var configurationRemove = function (e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._tearDownConfiguration(configuration);

                    productChangeHandler.call(this);
                    priceChangeHandler.call(this);
                };

                this.bind("configurations", "add", configurationAdd, this);
                this.bind("configurations", "remove", configurationRemove, this);
                this.bind("configurations", "reset", function () {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);
                }, this);

                this.bind("configurations", "item:priceChanged", priceChangeHandler, this);
                this.bind('configurations', 'item:configurationChanged', productChangeHandler, this);
                this.bind('change:configurations', productChangeHandler, this);
                this.bind('configurations', 'item:change:printArea', function () {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    productChangeHandler.call(this);
                }, this);

                this.bind('configurations', 'item:isValidChanged', function () {
                    this.trigger("configurationValidChanged");
                }, this);

                this.bind('configurations', 'item:change:offset', this._onConfigurationOffsetChanged, this);
                this.bind('change:productType', function () {
                    this.configurationsOnViewCache = {};
                }, this)
            },

            getDefaultAppearance: function () {
                var productType = this.$.productType;

                if (productType) {
                    return productType.getAppearanceById(this.get('appearance.id'));
                }

                return null;
            }.onChange('appearance', 'productType'),

            _onConfigurationOffsetChanged: function (e) {

                var manager = this.$.manager,
                    configuration = e.$.item;

                if (manager && manager.checkConfigurationOffset) {
                    manager.checkConfigurationOffset(this, configuration)
                }
            },

            configurationsOnViewError: function (view) {

                var errorValue = null,
                    configurations;

                if (!view) {
                    return null;
                }

                var viewId = view.$.id;

                var productType = this.$.productType;

                configurations = this.configurationsOnViewCache[viewId];
                if (!configurations && productType && productType.containsView(view)) {
                    this.configurationsOnViewCache[viewId] = configurations = this.getConfigurationsOnView(view);
                }

                if (!configurations) {
                    return null;
                }

                for (var i = 0; i < configurations.length; i++) {
                    var configuration = configurations[i];

                    if (!configuration.isValid() && configuration.$errors) {

                        for (var key in configuration.$errors.$) {
                            if (configuration.$errors.$.hasOwnProperty(key)) {
                                errorValue = configuration.$errors.$[key];
                                if (errorValue) {
                                    return {
                                        key: key,
                                        value: errorValue
                                    };
                                }
                            }
                        }
                    }
                }

                return null;

            }.on("configurationValidChanged"),

            _setUpConfiguration: function (configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.setUp(configuration);
            },

            _tearDownConfiguration: function (configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.tearDown(configuration);
            },

            _postConstruct: function () {

                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function (configuration) {
                        self._setUpConfiguration(configuration);
                    });
                }
            },

            _preDestroy: function () {
                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function (configuration) {
                        self._tearDownConfiguration(configuration);
                    });
                }
            },

            price: function () {

                // calculate price
                var price = new Price({
                    vatIncluded: this.get("productType.price.vatIncluded"),
                    currency: this.get('productType.price.currency')
                });
                this.$.configurations.each(function (configuration) {
                    price.add(configuration.price());
                });

                return price;

            }.on("priceChanged", ["productType", 'change:price']).onChange('productType'),

            _addConfiguration: function (configuration) {
                this.$.configurations.add(configuration);
            },

            removeExampleConfiguration: function () {
                if (this.get('restrictions.example') === true && this.$.configurations.size()) {
                    this.$.restrictions.example = false;
                    this.$.configurations.removeAt(0);
                }
            },

            getConfigurationsOnView: function (view) {

                view = view || this.$.view;

                var productType = this.$.productType;

                if (view && productType) {

                    if (productType.containsView(view)) {
                        return this.getConfigurationsOnPrintAreas(view.getPrintAreas());
                    } else {
                        throw new Error("View not on product type");
                    }

                }

                return [];

            },

            getConfigurationsOnPrintAreas: function (printAreas) {
                printAreas = printAreas || [];

                if (!(printAreas instanceof Array)) {
                    printAreas = [printAreas];
                }

                var ret = [];

                for (var i = 0; i < this.$.configurations.$items.length; i++) {
                    var configuration = this.$.configurations.$items[i];
                    if (_.contains(printAreas, configuration.$.printArea)) {
                        ret.push(configuration);
                    }
                }

                return ret;

            },

            fetch: function (options, callback) {
                var self = this,
                    fetchState = this._fetch.state;

                this.callBase(options, function (err) {
                    if (!err && fetchState !== 2) {
                        self.$originalProduct = self.clone();
                    }
                    callback && callback(err, self);
                });
            },

            hasChanges: function () {
                return !this.equals(this.$originalProduct);
            },

            equals: function (product) {
                return (this.$.configurations.isDeepEqual(product.$.configurations) &&
                    this.$.appearance.isDeepEqual(product.$.appearance) &&
                    this.$.productType.isDeepEqual(product.$.productType));
            },

            removeDuplicateConfigurations: function () {
                var configurations = this.$.configurations,
                    clonedConfigurations = configurations.clone();

                for (var i = 0; i < clonedConfigurations.length; i++) {
                    var config = clonedConfigurations.at(i);
                    for (var j = i + 1; j < clonedConfigurations.length; j++) {
                        var otherConfig = clonedConfigurations.at(j),
                            duplicate = config.isDeepEqual(otherConfig);

                        if (duplicate) {
                            configurations.removeAt(j);
                        }
                    }
                }
            },

            removeTooSmallConfigurations: function (configuration) {
                if (!configuration) {
                    return;
                }

                var self = this,
                    size = configuration.size(),
                    minSize = configuration.$.minSize;

                if (!size) {
                    return;
                }

                var width = size.$.width,
                    height = size.$.height;

                if (!width || !height || width < minSize || height < minSize ) {
                    self.$.configurations.remove(configuration);
                }
            },

            clean: function () {
                var self = this;
                self.removeExampleConfiguration();
                self.removeDuplicateConfigurations();
                self.eachConfig(self.cleanText, self);
                self.eachConfig(self.removeTooSmallConfigurations, self);
            },

            cleanText: function (configuration) {
                var self = this;
                var isTextConfig = configuration.isOnlyWhiteSpace && configuration.textChangedSinceCreation;
                if (!isTextConfig) {
                    return;
                }

                if (configuration.isOnlyWhiteSpace() || !configuration.textChangedSinceCreation()) {
                    self.$.configurations.remove(configuration);
                }
            },

            eachConfig: function (func, scope) {
                var self = this,
                    configurations = this.$.configurations,
                    tempConfigs = [].concat(configurations.$items);

                _.each(tempConfigs,func, scope);
            },

            save: function (options, callback) {
                if (this.$originalProduct) {
                    if (this.hasChanges()) {
                        this.set('id', undefined);
                    } else {
                        this.set({
                            id: this.$originalProduct.$.id,
                            href: this.$originalProduct.$.href
                        });

                        callback && callback(null, this);
                        return;
                    }
                }

                var self = this;

                if (!this.$.creator) {
                    if (this.$stage && this.$stage.$application.name) {
                        this.set("creator", this.$stage.$application.name);
                    }
                }

                flow()
                    .seq(function () {
                        self.clean();
                    })
                    .parEach(this.$.configurations.$items, function (configuration, cb) {
                        configuration.save(cb);
                    })
                    .seq(function (cb) {
                        ProductBase.prototype.save.call(self, options, cb);
                    })
                    .exec(function (err) {
                        if (!err) {
                            var clone = self.clone();
                            // change original product against the clone in the entity
                            // so that the application can work with the original product
                            self.$context.removeEntityFromCache(self);
                            self.$context.addEntityToCache(clone);

                            self.$originalProduct = clone;
                        } else {
                            err = ProductCreationError.createFromResponse(err);
                        }

                        callback && callback(err, self);
                    });

            },

            /***
             *
             * @param [options]
             * @param callback
             */
            init: function (options, callback) {

                if (options instanceof Function) {
                    callback = options;
                    options = {};
                }

                if (this.initialized) {
                    callback && callback(null, this);
                    return;
                }
                var self = this;

                flow()
                    .seq(function (cb) {
                        if (self.isNew()) {
                            cb();
                        } else {
                            self.fetch(null, cb);
                        }
                    })
                    .seq(function (cb) {
                        var productType = self.$.productType;
                        productType.fetch(null, cb);
                    })
                    .seq(function () {
                        var productType = self.$.productType;

                        var appearance;

                        if (self.$.appearance) {
                            appearance = productType.getAppearanceById(self.$.appearance.$.id);
                        }

                        appearance = appearance || productType.getDefaultAppearance();

                        self.set({
                            appearance: appearance,
                            view: self.$.view || productType.getViewById(self.get("defaultValues.defaultView.id")) || productType.getDefaultView()
                        });
                    })
                    .seq(function (cb) {
                        flow()
                            .parEach(self.$.configurations.$items, function (configuration, cb) {
                                self._setUpConfiguration(configuration);
                                configuration.init(options, cb);
                            })
                            .exec(function (err) {
                                cb(err);
                            });
                    })
                    .exec(function (err) {
                        self.trigger("productInitialized");
                        self.trigger("priceChanged");

                        if (err) {
                            callback && callback(err);
                        } else {
                            self.initialized = true;
                            callback && callback(null, self);
                        }
                    });
            },

            getAvailableAppearances: function () {
                var productType = this.$.productType;
                if (productType) {
                    var appearances = productType.getAvailableAppearances(),
                        configurations = this.$.configurations;
                    if (configurations) {
                        var ret = new List(appearances.toArray());
                        configurations.each(function (config) {
                            appearances.each(function (appearance) {
                                if (ret.contains(appearance)) {
                                    var printTypes = config.getPossiblePrintTypes(appearance);
                                    if (!printTypes || !printTypes.length) {
                                        ret.remove(appearance);
                                    }
                                }
                            });
                        });
                        return ret;
                    }
                    return appearances;
                }
                return null;
            }.onChange('productType.stockStates'),

            isReadyForCompose: function () {
                var ready = true;

                if (this.$.configurations) {
                    this.$.configurations.each(function (configuration) {
                        ready = ready && configuration.isReadyForCompose();
                    });
                }

                return ready;
            },

            getContainedMasks: function () {
                var configs = this.$.configurations;
                var masks = [];
                for (var i = 0; i < configs.length; i++) {
                    var config = configs.$items[i];
                    if (config.$.afterEffect) {
                        masks.push(config.$.afterEffect);
                    }
                }

                return masks;
            },

            _commitChangedAttributes: function (attributes) {
                this.callBase();
                if (attributes.hasOwnProperty("appearance") || attributes.hasOwnProperty("productType")) {
                    this.trigger("productChanged");
                }
            },

            sync: function () {
                var ret = this.callBase();
                if (ret) {
                    this._$source.initialized = false;
                }
                return ret;
            }
        });
    });
<commit_msg>specified keys for deepEqual<commit_after>define(['sprd/model/ProductBase', 'js/core/List', 'sprd/data/ConfigurationTypeResolver', 'sprd/entity/DesignConfiguration', 'sprd/entity/TextConfiguration', 'sprd/entity/SpecialTextConfiguration', 'sprd/entity/Price', 'js/data/TypeResolver', 'js/data/Entity', "underscore", "flow", "sprd/manager/IProductManager", "sprd/error/ProductCreationError", 'sprd/model/ProductType', 'sprd/entity/Appearance', 'sprd/entity/BendingTextConfiguration'],
    function(ProductBase, List, ConfigurationTypeResolver, DesignConfiguration, TextConfiguration, SpecialTextConfiguration, Price, TypeResolver, Entity, _, flow, IProductManager, ProductCreationError, ProductType, Appearance, BendingTextConfiguration) {

        var undefined;

        return ProductBase.inherit("sprd.model.Product", {

            schema: {
                productType: ProductType,
                configurations: [new ConfigurationTypeResolver({
                    mapping: {
                        "design": DesignConfiguration,
                        "text": TextConfiguration,
                        "specialText": SpecialTextConfiguration,
                        "bendingText": BendingTextConfiguration
                    }
                })],
                appearance: {
                    type: Appearance,
                    isReference: true
                },
                restrictions: Object,
                defaultValues: Object,
                price: {
                    type: Price,
                    generated: true
                },
                creator: String,
                templateId: {
                    type: String,
                    required: false
                }
            },

            defaults: {
                configurations: List,
                creator: null
            },

            inject: {
                manager: IProductManager
            },

            ctor: function() {
                this.configurationsOnViewCache = {};

                this.callBase();

                var priceChangeHandler = function() {
                    this.trigger("priceChanged");
                };

                var productChangeHandler = function() {
                    this.trigger("productChanged");
                };

                var configurationAdd = function(e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._setUpConfiguration(configuration);

                    if (configuration instanceof TextConfiguration) {
                        configuration._composeText();
                    }
                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);

                };

                var configurationRemove = function(e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._tearDownConfiguration(configuration);

                    productChangeHandler.call(this);
                    priceChangeHandler.call(this);
                };

                this.bind("configurations", "add", configurationAdd, this);
                this.bind("configurations", "remove", configurationRemove, this);
                this.bind("configurations", "reset", function() {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);
                }, this);

                this.bind("configurations", "item:priceChanged", priceChangeHandler, this);
                this.bind('configurations', 'item:configurationChanged', productChangeHandler, this);
                this.bind('change:configurations', productChangeHandler, this);
                this.bind('configurations', 'item:change:printArea', function() {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    productChangeHandler.call(this);
                }, this);

                this.bind('configurations', 'item:isValidChanged', function() {
                    this.trigger("configurationValidChanged");
                }, this);

                this.bind('configurations', 'item:change:offset', this._onConfigurationOffsetChanged, this);
                this.bind('change:productType', function() {
                    this.configurationsOnViewCache = {};
                }, this)
            },

            getDefaultAppearance: function() {
                var productType = this.$.productType;

                if (productType) {
                    return productType.getAppearanceById(this.get('appearance.id'));
                }

                return null;
            }.onChange('appearance', 'productType'),

            _onConfigurationOffsetChanged: function(e) {

                var manager = this.$.manager,
                    configuration = e.$.item;

                if (manager && manager.checkConfigurationOffset) {
                    manager.checkConfigurationOffset(this, configuration)
                }
            },

            configurationsOnViewError: function(view) {

                var errorValue = null,
                    configurations;

                if (!view) {
                    return null;
                }

                var viewId = view.$.id;

                var productType = this.$.productType;

                configurations = this.configurationsOnViewCache[viewId];
                if (!configurations && productType && productType.containsView(view)) {
                    this.configurationsOnViewCache[viewId] = configurations = this.getConfigurationsOnView(view);
                }

                if (!configurations) {
                    return null;
                }

                for (var i = 0; i < configurations.length; i++) {
                    var configuration = configurations[i];

                    if (!configuration.isValid() && configuration.$errors) {

                        for (var key in configuration.$errors.$) {
                            if (configuration.$errors.$.hasOwnProperty(key)) {
                                errorValue = configuration.$errors.$[key];
                                if (errorValue) {
                                    return {
                                        key: key,
                                        value: errorValue
                                    };
                                }
                            }
                        }
                    }
                }

                return null;

            }.on("configurationValidChanged"),

            _setUpConfiguration: function(configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.setUp(configuration);
            },

            _tearDownConfiguration: function(configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.tearDown(configuration);
            },

            _postConstruct: function() {

                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function(configuration) {
                        self._setUpConfiguration(configuration);
                    });
                }
            },

            _preDestroy: function() {
                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function(configuration) {
                        self._tearDownConfiguration(configuration);
                    });
                }
            },

            price: function() {

                // calculate price
                var price = new Price({
                    vatIncluded: this.get("productType.price.vatIncluded"),
                    currency: this.get('productType.price.currency')
                });
                this.$.configurations.each(function(configuration) {
                    price.add(configuration.price());
                });

                return price;

            }.on("priceChanged", ["productType", 'change:price']).onChange('productType'),

            _addConfiguration: function(configuration) {
                this.$.configurations.add(configuration);
            },

            removeExampleConfiguration: function() {
                if (this.get('restrictions.example') === true && this.$.configurations.size()) {
                    this.$.restrictions.example = false;
                    this.$.configurations.removeAt(0);
                }
            },

            getConfigurationsOnView: function(view) {

                view = view || this.$.view;

                var productType = this.$.productType;

                if (view && productType) {

                    if (productType.containsView(view)) {
                        return this.getConfigurationsOnPrintAreas(view.getPrintAreas());
                    } else {
                        throw new Error("View not on product type");
                    }

                }

                return [];

            },

            getConfigurationsOnPrintAreas: function(printAreas) {
                printAreas = printAreas || [];

                if (!(printAreas instanceof Array)) {
                    printAreas = [printAreas];
                }

                var ret = [];

                for (var i = 0; i < this.$.configurations.$items.length; i++) {
                    var configuration = this.$.configurations.$items[i];
                    if (_.contains(printAreas, configuration.$.printArea)) {
                        ret.push(configuration);
                    }
                }

                return ret;

            },

            fetch: function(options, callback) {
                var self = this,
                    fetchState = this._fetch.state;

                this.callBase(options, function(err) {
                    if (!err && fetchState !== 2) {
                        self.$originalProduct = self.clone();
                    }
                    callback && callback(err, self);
                });
            },

            hasChanges: function() {
                return !this.equals(this.$originalProduct);
            },

            equals: function(product) {
                return (this.$.configurations.isDeepEqual(product.$.configurations) &&
                this.$.appearance.isDeepEqual(product.$.appearance) &&
                this.$.productType.isDeepEqual(product.$.productType));
            },

            removeDuplicateConfigurations: function() {
                var configurations = this.$.configurations,
                    clonedConfigurations = configurations.clone();

                for (var i = 0; i < clonedConfigurations.length; i++) {
                    var config = clonedConfigurations.at(i);
                    for (var j = i + 1; j < clonedConfigurations.length; j++) {
                        var otherConfig = clonedConfigurations.at(j),
                            keys = ["text", "font", "_size", "align", "type", "design", "printArea", "offset", "scale", "rotation"],
                            duplicate = config.isDeepEqual(otherConfig, keys);

                        if (duplicate) {
                            configurations.removeAt(j);
                        }
                    }
                }
            },

            removeTooSmallConfigurations: function(configuration) {
                if (!configuration) {
                    return;
                }

                var self = this,
                    size = configuration.size(),
                    minSize = configuration.$.minSize;

                if (!size) {
                    return;
                }

                var width = size.$.width,
                    height = size.$.height;

                if (!width || !height || width < minSize || height < minSize) {
                    self.$.configurations.remove(configuration);
                }
            },

            clean: function() {
                var self = this;
                self.removeExampleConfiguration();
                self.removeDuplicateConfigurations();
                self.eachConfig(self.cleanText, self);
                self.eachConfig(self.removeTooSmallConfigurations, self);
            },

            cleanText: function(configuration) {
                var self = this;
                var isTextConfig = configuration.isOnlyWhiteSpace && configuration.textChangedSinceCreation;
                if (!isTextConfig) {
                    return;
                }

                if (configuration.isOnlyWhiteSpace() || !configuration.textChangedSinceCreation()) {
                    self.$.configurations.remove(configuration);
                }
            },

            eachConfig: function(func, scope) {
                var self = this,
                    configurations = this.$.configurations,
                    tempConfigs = [].concat(configurations.$items);

                _.each(tempConfigs, func, scope);
            },

            save: function(options, callback) {
                if (this.$originalProduct) {
                    if (this.hasChanges()) {
                        this.set('id', undefined);
                    } else {
                        this.set({
                            id: this.$originalProduct.$.id,
                            href: this.$originalProduct.$.href
                        });

                        callback && callback(null, this);
                        return;
                    }
                }

                var self = this;

                if (!this.$.creator) {
                    if (this.$stage && this.$stage.$application.name) {
                        this.set("creator", this.$stage.$application.name);
                    }
                }

                flow()
                    .seq(function() {
                        self.clean();
                    })
                    .parEach(this.$.configurations.$items, function(configuration, cb) {
                        configuration.save(cb);
                    })
                    .seq(function(cb) {
                        ProductBase.prototype.save.call(self, options, cb);
                    })
                    .exec(function(err) {
                        if (!err) {
                            var clone = self.clone();
                            // change original product against the clone in the entity
                            // so that the application can work with the original product
                            self.$context.removeEntityFromCache(self);
                            self.$context.addEntityToCache(clone);

                            self.$originalProduct = clone;
                        } else {
                            err = ProductCreationError.createFromResponse(err);
                        }

                        callback && callback(err, self);
                    });

            },

            /***
             *
             * @param [options]
             * @param callback
             */
            init: function(options, callback) {

                if (options instanceof Function) {
                    callback = options;
                    options = {};
                }

                if (this.initialized) {
                    callback && callback(null, this);
                    return;
                }
                var self = this;

                flow()
                    .seq(function(cb) {
                        if (self.isNew()) {
                            cb();
                        } else {
                            self.fetch(null, cb);
                        }
                    })
                    .seq(function(cb) {
                        var productType = self.$.productType;
                        productType.fetch(null, cb);
                    })
                    .seq(function() {
                        var productType = self.$.productType;

                        var appearance;

                        if (self.$.appearance) {
                            appearance = productType.getAppearanceById(self.$.appearance.$.id);
                        }

                        appearance = appearance || productType.getDefaultAppearance();

                        self.set({
                            appearance: appearance,
                            view: self.$.view || productType.getViewById(self.get("defaultValues.defaultView.id")) || productType.getDefaultView()
                        });
                    })
                    .seq(function(cb) {
                        flow()
                            .parEach(self.$.configurations.$items, function(configuration, cb) {
                                self._setUpConfiguration(configuration);
                                configuration.init(options, cb);
                            })
                            .exec(function(err) {
                                cb(err);
                            });
                    })
                    .exec(function(err) {
                        self.trigger("productInitialized");
                        self.trigger("priceChanged");

                        if (err) {
                            callback && callback(err);
                        } else {
                            self.initialized = true;
                            callback && callback(null, self);
                        }
                    });
            },

            getAvailableAppearances: function() {
                var productType = this.$.productType;
                if (productType) {
                    var appearances = productType.getAvailableAppearances(),
                        configurations = this.$.configurations;
                    if (configurations) {
                        var ret = new List(appearances.toArray());
                        configurations.each(function(config) {
                            appearances.each(function(appearance) {
                                if (ret.contains(appearance)) {
                                    var printTypes = config.getPossiblePrintTypes(appearance);
                                    if (!printTypes || !printTypes.length) {
                                        ret.remove(appearance);
                                    }
                                }
                            });
                        });
                        return ret;
                    }
                    return appearances;
                }
                return null;
            }.onChange('productType.stockStates'),

            isReadyForCompose: function() {
                var ready = true;

                if (this.$.configurations) {
                    this.$.configurations.each(function(configuration) {
                        ready = ready && configuration.isReadyForCompose();
                    });
                }

                return ready;
            },

            getContainedMasks: function() {
                var configs = this.$.configurations;
                var masks = [];
                for (var i = 0; i < configs.length; i++) {
                    var config = configs.$items[i];
                    if (config.$.afterEffect) {
                        masks.push(config.$.afterEffect);
                    }
                }

                return masks;
            },

            _commitChangedAttributes: function(attributes) {
                this.callBase();
                if (attributes.hasOwnProperty("appearance") || attributes.hasOwnProperty("productType")) {
                    this.trigger("productChanged");
                }
            },

            sync: function() {
                var ret = this.callBase();
                if (ret) {
                    this._$source.initialized = false;
                }
                return ret;
            }
        });
    });
<|endoftext|>


<commit_before>; // defensive programming: script may be concatenated with others

/*
 * ScrollDepthTracker | v0.1
 * Copyright (c) 2014 Ole Frank Jensen
 * Licensed under the MIT license
 */

var ScrollDepthTracker = (function() {

    "use strict";

    var scrollDepthIterator = 0;
    var scrollDepth = -1;
    var scrollDelay;
    var scrollTimeStart;

    /**
     * Object to hold default settings
     * @element: DOM element to track
     * @scrollDepthInterval: size of slices to measure content with
     * @eventHandler: function to handle event broadcasting
     */
    var settings = {
        element: document.querySelector(".article"),
        scrollDepthInterval: 20,
        eventHandler: broadcastEvent
    };

    /**
     * Central function for binding all event listeners
     */
    function bindDOMEvents() {

        // when user scrolls: calculate scroll depth
        // calculation happens after delay (150 ms)
        window.onscroll = function() {
            clearTimeout(scrollDelay);
            scrollDelay = setTimeout(function() {

                var sd = getScrollDepth();
                if (sd > scrollDepth) {
                    scrollDepth = sd;
                    settings.eventHandler(getScrollDepthEventMessage(scrollDepth));
                }

            }, 150);
        };

    }

    /**
     * Calculate scroll depth
     * Scroll depth can be in range 0-1
     * Scroll depth is limited to intervals defined in settings
     * @return result: current scroll depth (number)
     */
    function getScrollDepth() {
        var result;
        var rect = settings.element.getBoundingClientRect();
        var elementTop = rect.top;
        var elementHeight = rect.height;
        var viewBottom = window.innerHeight;
        var slicePct = settings.scrollDepthInterval / 100;

        // if element is in view
        if (elementTop <= viewBottom) {
            result = (viewBottom - elementTop) / elementHeight;

            // store time for first scroll event
            if (scrollTimeStart === undefined) {
                scrollTimeStart = new Date();
            }

            // round down to nearest
            result = result > 1 ? 1 : ~~(result / slicePct) * slicePct;
        }

        return result;
    }

    /**
     * Generate scroll depth event string
     * Event string consists of scroll depths and time stamps
     * Example: "sd_20=1500"
     * Example: "sd_40=2500&sd_60=2500&sd_80=2500"
     * @param depth: current scroll depth
     * @return message: string describing scroll depth event
     */
    function getScrollDepthEventMessage(depth) {
        var messageTxt;
        var message;
        var slicePct = settings.scrollDepthInterval / 100;
        var depthTmp = 0;
        var timeDiff = new Date().getTime() - scrollTimeStart.getTime();

        while (depthTmp < depth) {
            depthTmp = (scrollDepthIterator * slicePct) < 1 ? (scrollDepthIterator * slicePct) : 1;
            messageTxt = "sd_" + Math.round(depthTmp * 100) + "=" + timeDiff;

            if (message === undefined) {
                message = messageTxt;
            }
            else {
                message += "&" + messageTxt;
            }

            scrollDepthIterator++;
        }

        // first scroll depth measure (fake)
        if (scrollDepthIterator === 0) {
            message = "sd_0=0"
            scrollDepthIterator++;
        }

        return message;
    }

    /**
     * Send events
     * @param message: event message
     */
    function broadcastEvent(message) {
        console.log("simulate event: " + message);
    }

    /**
     * Merge user settings with default settings
     * User settings overwrite default settings where applied
     * @param userSettings: settings the user has specified
     */
    function mergeSettings(userSettings) {
        for (var key in userSettings) {
            settings[key] = userSettings[key];
        }
    }

    /**
     * Start tracking attention!
     * @param userSettings: settings the user has specified
     */
    function init(userSettings) {
        mergeSettings(userSettings);
        bindDOMEvents();
    }

    // define plugin interface
    return {
        init: init
    };

})();
<commit_msg>change event listeners to DOM Level 2<commit_after>; // defensive programming: script may be concatenated with others

/*
 * ScrollDepthTracker | v0.1
 * Copyright (c) 2014 Ole Frank Jensen
 * Licensed under the MIT license
 */

var ScrollDepthTracker = (function() {

    "use strict";

    var scrollDepthIterator = 0;
    var scrollDepth = -1;
    var scrollDelay;
    var scrollTimeStart;

    /**
     * Object to hold default settings
     * @element: DOM element to track
     * @scrollDepthInterval: size of slices to measure content with
     * @eventHandler: function to handle event broadcasting
     */
    var settings = {
        element: document.querySelector(".article"),
        scrollDepthInterval: 20,
        eventHandler: broadcastEvent
    };

    /**
     * Central function for binding all event listeners
     */
    function bindDOMEvents() {

        // handle cross browser event listeners
        var addEventListener = function (element, type, handler) {
            if (element.addEventListener) {
                return element.addEventListener(type, handler, false)
            }
            else if (element.attachEvent) {
                return element.attachEvent("on" + type, handler)
            }
        };

        // add eventlisteners
        addEventListener(window, "scroll", onScrollHandler);

        // when user scrolls: calculate scroll depth
        // calculation happens after delay (150 ms)
        function onScrollHandler() {
            clearTimeout(scrollDelay);
            scrollDelay = setTimeout(function() {

                var sd = getScrollDepth();
                if (sd > scrollDepth) {
                    scrollDepth = sd;
                    settings.eventHandler(getScrollDepthEventMessage(scrollDepth));
                }

            }, 150);
        }

    }

    /**
     * Calculate scroll depth
     * Scroll depth can be in range 0-1
     * Scroll depth is limited to intervals defined in settings
     * @return result: current scroll depth (number)
     */
    function getScrollDepth() {
        var result;
        var rect = settings.element.getBoundingClientRect();
        var elementTop = rect.top;
        var elementHeight = rect.height;
        var viewBottom = window.innerHeight;
        var slicePct = settings.scrollDepthInterval / 100;

        // if element is in view
        if (elementTop <= viewBottom) {
            result = (viewBottom - elementTop) / elementHeight;

            // store time for first scroll event
            if (scrollTimeStart === undefined) {
                scrollTimeStart = new Date();
            }

            // round down to nearest
            result = result > 1 ? 1 : ~~(result / slicePct) * slicePct;
        }

        return result;
    }

    /**
     * Generate scroll depth event string
     * Event string consists of scroll depths and time stamps
     * Example: "sd_20=1500"
     * Example: "sd_40=2500&sd_60=2500&sd_80=2500"
     * @param depth: current scroll depth
     * @return message: string describing scroll depth event
     */
    function getScrollDepthEventMessage(depth) {
        var messageTxt;
        var message;
        var slicePct = settings.scrollDepthInterval / 100;
        var depthTmp = 0;
        var timeDiff = new Date().getTime() - scrollTimeStart.getTime();

        while (depthTmp < depth) {
            depthTmp = (scrollDepthIterator * slicePct) < 1 ? (scrollDepthIterator * slicePct) : 1;
            messageTxt = "sd_" + Math.round(depthTmp * 100) + "=" + timeDiff;

            if (message === undefined) {
                message = messageTxt;
            }
            else {
                message += "&" + messageTxt;
            }

            scrollDepthIterator++;
        }

        // first scroll depth measure (fake)
        if (scrollDepthIterator === 0) {
            message = "sd_0=0"
            scrollDepthIterator++;
        }

        return message;
    }

    /**
     * Send events
     * @param message: event message
     */
    function broadcastEvent(message) {
        console.log("simulate event: " + message);
    }

    /**
     * Merge user settings with default settings
     * User settings overwrite default settings where applied
     * @param userSettings: settings the user has specified
     */
    function mergeSettings(userSettings) {
        for (var key in userSettings) {
            settings[key] = userSettings[key];
        }
    }

    /**
     * Start tracking attention!
     * @param userSettings: settings the user has specified
     */
    function init(userSettings) {
        mergeSettings(userSettings);
        bindDOMEvents();
    }

    // define plugin interface
    return {
        init: init
    };

})();
<|endoftext|>