<commit_before>/**
 * MIT LICENSE
 *
 * Compatibility: ie8+, other modern browsers
 * Note: Include jQuery on the page for ie8 to be able to fire hashbang-parse and hashbang-serialize events.
 *
 * Read:
 * http://googlewebmastercentral.blogspot.cz/2009/10/proposal-for-making-ajax-crawlable.html
 * https://developers.google.com/webmasters/ajax-crawling/docs/specification
 *
 * Supports:
 * - Hash syntax: #!/PATH/PART?VARIABLES
 * - Hash syntax: #!VARIABLES
 * - Arrays: var[]=val&var[]=val or var[0]=val&var[1]=val
 * - Objects: var[prop]=val&var[prop2]=val
 *
 * Use following events to hook on hashbang:
 *
 * $(window).on('hashbang-init',  function()); // upon window.hashbang object initialization
 * $(window).on('hashbang-parse',  function()); // upon load and URL hash change and window.hashbang object update
 * $(window).on('hashbang-serialize',  function()); // upon window.hashbang object change and URL hash update
 *
 * The object window.hashbang is readable/writable and keeps parsed
 * representation of URL hashbang hash.
 *
 * window.hashbang.something={"any": 1, "else": [{"a":1}, {"b":2}]};
 *
 * If the URL hash conains unescape question mark then the path component before
 * question mark is stored in window.hashbang["#path"] special read/write property.
 *
 * If you want to use '#' insead of '#!' then run this code before you include hashbang script:
 *   window.hashbangSeparator="#";
 *
 * Two usefull functions are provided for creation and parsing of hashbang hashes:
 *   window.hashbangParse(hash):object
 *   window.hashbangSerialize(object):hash
 *
 * Example:
 *   $(link).attr('href', window.hashbangSerialize({"a": 1, "b": 2}));
 *
 * @package    Hashbang Object
 * @author     Daniel Sevcik <sevcik@webdevelopers.cz>
 * @version    1.0
 * @copyright  2014 Daniel Sevcik
 * @since      2014-07-16 22:22:34 UTC
 */
(function() {
    'use strict';

    var log=function() {
        arguments[0]='Hashbang Object: ' + arguments[0];
        if (console && console.log) {
            if (console.log.apply) {
                console.log.apply(console, arguments);
            } else { // IE8
                console.log(arguments[0]);
            }
        }
    };
    log("Loading... " + (typeof Object.observe == 'function' ? 'Object.observe available' : ''));

    if ('hashbang' in window) {
        log("ERROR: window.hashbang already exists! The Hashbang script was included twice or there is other conflicting app.");
        return;
    }

    window.hashbangSeparator=window.hashbangSeparator || '#!';

    var lock=false;
    var ourHash;
    var lastSerialized;
    var hasHashbangFormat=false;
    var updateTimeout;
    var hashbangInterval;

    function setHash(hash) {
        ourHash=hash;
        // window.location.hash=ourHash; - FF unescapes the values
        window.location.href=(window.location.href + '#').replace(/#.*$/, hash);
        log("Set new hash: " + hash + " | current hash: " + getHash());
    }

    function getHash() {
        // window.location.hash - FF unescapes it!
        return window.location.href.replace(/^[^#]+/, '');
    }

    function trigger(name) {
        log("Triggering event " + name + " on window object...");
        var el=document;
        var e;
        if (typeof jQuery == 'function') {
            jQuery('body').trigger(name);
        } else if (document.createEventObject) { // IE
            e=document.createEventObject();
            try {
                el.fireEvent(name, e);
            } catch (e) { // IE8 custom events fail!
                log("WARNING: Include jQuery on the page to fire events in archaic browsers.");
            }
        } else {
            e=document.createEvent('HTMLEvents');
            e.initEvent(name, true, true);
            el.dispatchEvent(e);
        }
    }

    function listen(obj, name, callback) {
        if(obj.addEventListener) {
            // log("Using addEventListener for " + name);
            obj.addEventListener(name, callback, false);
        } else if (obj.attachEvent) {
            // log("Using attachEvent for " + name);
            obj.attachEvent(name, callback, false);
        }
    }

    function observe(obj) {
        // Observe Object
        if (typeof Object.observe == 'function') {
            Object.observe(obj, updateHash);
            for (var i in obj) {
                if (typeof obj[i] == 'object') {
                    observe(obj[i]);
                }
            }
        } else {
            clearInterval(hashbangInterval);
            hashbangInterval=setInterval(function() {
                var serialized=JSON.stringify(window.hashbang);
                if (lastSerialized == serialized) return; // same
                // log("Updating hash... last: " + lastSerialized + ", current: " + serialized);
                lastSerialized=serialized;
                updateHash();
            }, 500);
        }
        return obj;
    }

    function updateHash() { // Delay because when extensive window.hashbang modification the handler is called too often
        clearTimeout(updateTimeout);
        updateTimeout=setTimeout(function() {
            var hash=window.hashbangSerialize(window.hashbang);
            setHash(hash);
            observe(window.hashbang);
            trigger('hashbang-serialize');
        }, 50);
    }

    window.hashbangSerialize=function(what) {
        function serialize(keys, val) {
            // log('Serializing', keys, val);
            var ret=[], i;

            switch (val instanceof Array ? 'array' : typeof val) {
            case 'boolean':
            case 'number':
            case 'string':
                var key=(keys.shift() || 'nokey') + (keys.length && '[' + keys.join('][') + ']' || '');
                ret.push(key + '=' + encodeURIComponent(val));
                break;
            case 'array':
                for (i=0; i < val.length; i++) {
                    keys.push(typeof val[i] == 'object' ? i : '');
                    ret=ret.concat(serialize(keys.concat([]), val[i]));
                    keys.pop();
                }
                break;
            case 'object':
                for (i in val) {
                    if (i == '#path') continue;
                    keys.push(encodeURIComponent(i));
                    ret=ret.concat(serialize(keys.concat([]), val[i]));
                    keys.pop();
                }
                break;
            default:
                log("The value type '" + (typeof val) +"' is not supported.");
            }
            return ret;
        }

        var path=(what['#path'] || '');
        var hash=serialize([], what).join('&');
        return hash || path ? window.hashbangSeparator + path + (path && hash ? '?' : '') + hash : '#';
    };

    function toArray(obj) {
        var i;
        if (typeof obj == 'object') {
            var hb=obj.__isArray__ || 0;
            delete obj.__isArray__;
            var arr=[];
            for (i in obj) {
                obj[i]=toArray(obj[i]);
                arr.push(obj[i]);
            }
            if (arr.length == hb) { // convert to plain array
                var seq=true;
                for (i=0; i < hb; i++) { // make sure the numbers are sequential
                    if (typeof obj[i] == 'undefined') return obj; // not an array
                }
                obj=arr;
            }
        }
        return obj;
    }

    function updateObject() {
        if (getHash() == ourHash) {
            return; // triggered by our updateHash()
        }
        var fireEvent='hashbang' in window ? 'hashbang-parse' : 'hashbang-init';
        var hash=getHash();
        ourHash=hash;
        hasHashbangFormat=isHashbang(hash);
        if (!hasHashbangFormat) fireEvent='hashbang-unparsable';
        var obj=window.hashbangParse(hash);

        lock=true;
        window.hashbang=obj;
        lastSerialized=JSON.stringify(window.hashbang);
        lock=false;

        observe(window.hashbang);
        log("Object updated (" + fireEvent + "): " + JSON.stringify(obj));
        trigger(fireEvent);
    }

    function isHashbang(hash) {
        return (hash.substr(0, window.hashbangSeparator.length) == window.hashbangSeparator && hash.substr(2));
    }

    window.hashbangParse=function (hash) {
        var obj={};

        if (isHashbang(hash)) {
            var pointer;
            var pathRE=/^([/a-z0-9. %_~!$'()*+,;:@-]+)(\?|$)/i; // no '&=' even though path part allows it
            hash=hash.substr(window.hashbangSeparator.length); // remove '#!'
            if (hash.match(pathRE)) {
                obj["#path"]=hash.match(pathRE)[1];
                hash=hash.replace(pathRE, '');
            }
            var params=hash ? hash.split('&') : [];
            for (var i=0; i < params.length; i++) {
                var param=params[i].split('=');
                var key=param.shift().replace(/]$/, '');
                var val=decodeURIComponent(param.join('='));

                var keys=key.replace(/\]\[/g, '[').split('[');
                pointer=obj;
                while (keys.length) {
                    var prop=decodeURIComponent(keys.shift()) || pointer.__isArray__;
                    if ((prop + '').match(/^[0-9]+$/) && typeof pointer[prop] == 'undefined') {
                        pointer.__isArray__++; // hint possible array
                    }
                    if (keys.length) {
                        if (typeof pointer[prop] != 'object') {
                            pointer[prop]={"__isArray__": 0};
                        }
                        pointer=pointer[prop];
                    } else {
                        pointer[prop]=val;
                    }
                }
            }
            obj=toArray(obj);
        }

        return obj;
    };

    // First run
    updateObject(); // before Object.observe

    observe(window.hashbang);
    if (typeof Object.observe == 'function' && typeof window.__defineSetter__ == 'function') {
        log("Watching window.hashbang");
        var watchValue=window.hashbang;
        window.__defineGetter__('hashbang', function() {return watchValue;});
        window.__defineSetter__('hashbang', function(val) {watchValue=val; !lock && updateHash(); return watchValue;});
    }

    // Observe Hash
    if ("onhashchange" in window) {
        listen(window, "hashchange", updateObject);
        listen(window, "onhashchange", updateObject); // ie8
        // window.onhashchange=updateObject; // ie8
    } else {
        throw ("Hashbang: The event hashchange is not supported!");
    }
})();
<commit_msg>Hashbang's path support requires '/' in front of path.<commit_after>/**
 * MIT LICENSE
 *
 * Compatibility: ie8+, other modern browsers
 * Note: Include jQuery on the page for ie8 to be able to fire hashbang-parse and hashbang-serialize events.
 *
 * Read:
 * http://googlewebmastercentral.blogspot.cz/2009/10/proposal-for-making-ajax-crawlable.html
 * https://developers.google.com/webmasters/ajax-crawling/docs/specification
 *
 * Supports:
 * - Hash syntax: #!/PATH/PART?VARIABLES
 * - Hash syntax: #!VARIABLES
 * - Arrays: var[]=val&var[]=val or var[0]=val&var[1]=val
 * - Objects: var[prop]=val&var[prop2]=val
 *
 * Use following events to hook on hashbang:
 *
 * $(window).on('hashbang-init',  function()); // upon window.hashbang object initialization
 * $(window).on('hashbang-parse',  function()); // upon load and URL hash change and window.hashbang object update
 * $(window).on('hashbang-serialize',  function()); // upon window.hashbang object change and URL hash update
 *
 * The object window.hashbang is readable/writable and keeps parsed
 * representation of URL hashbang hash.
 *
 * window.hashbang.something={"any": 1, "else": [{"a":1}, {"b":2}]};
 *
 * If the URL hash conains unescape question mark then the path component (starting with '/') before
 * question mark is stored in window.hashbang["#path"] special read/write property.
 *
 * Path component must start with '/'.
 *
 * #!/my/path?var1=1
 * #!/my/path
 *
 *
 * If you want to use '#' insead of '#!' then run this code before you include hashbang script:
 *   window.hashbangSeparator="#";
 *
 * Two usefull functions are provided for creation and parsing of hashbang hashes:
 *   window.hashbangParse(hash):object
 *   window.hashbangSerialize(object):hash
 *
 * Example:
 *   $(link).attr('href', window.hashbangSerialize({"a": 1, "b": 2}));
 *
 * @package    Hashbang Object
 * @author     Daniel Sevcik <sevcik@webdevelopers.cz>
 * @version    1.0
 * @copyright  2014 Daniel Sevcik
 * @since      2014-07-16 22:22:34 UTC
 */
(function() {
    'use strict';

    var log=function() {
	arguments[0]='Hashbang Object: ' + arguments[0];
	if (console && console.log) {
	    if (console.log.apply) {
		console.log.apply(console, arguments);
	    } else { // IE8
		console.log(arguments[0]);
	    }
	}
    };
    log("Loading... " + (typeof Object.observe == 'function' ? 'Object.observe available' : ''));

    if ('hashbang' in window) {
	log("ERROR: window.hashbang already exists! The Hashbang script was included twice or there is other conflicting app.");
	return;
    }

    window.hashbangSeparator=window.hashbangSeparator || '#!';

    var lock=false;
    var ourHash;
    var lastSerialized;
    var hasHashbangFormat=false;
    var updateTimeout;
    var hashbangInterval;

    function setHash(hash) {
	ourHash=hash;
	// window.location.hash=ourHash; - FF unescapes the values
	window.location.href=(window.location.href + '#').replace(/#.*$/, hash);
	log("Set new hash: " + hash + " | current hash: " + getHash());
    }

    function getHash() {
	// window.location.hash - FF unescapes it!
	return window.location.href.replace(/^[^#]+/, '');
    }

    function trigger(name) {
	log("Triggering event " + name + " on window object...");
	var el=document;
	var e;
	if (typeof jQuery == 'function') {
	    jQuery('body').trigger(name);
	} else if (document.createEventObject) { // IE
	    e=document.createEventObject();
	    try {
		el.fireEvent(name, e);
	    } catch (e) { // IE8 custom events fail!
		log("WARNING: Include jQuery on the page to fire events in archaic browsers.");
	    }
	} else {
	    e=document.createEvent('HTMLEvents');
	    e.initEvent(name, true, true);
	    el.dispatchEvent(e);
	}
    }

    function listen(obj, name, callback) {
	if(obj.addEventListener) {
	    // log("Using addEventListener for " + name);
	    obj.addEventListener(name, callback, false);
	} else if (obj.attachEvent) {
	    // log("Using attachEvent for " + name);
	    obj.attachEvent(name, callback, false);
	}
    }

    function observe(obj) {
	// Observe Object
	if (typeof Object.observe == 'function') {
	    Object.observe(obj, updateHash);
	    for (var i in obj) {
		if (typeof obj[i] == 'object') {
		    observe(obj[i]);
		}
	    }
	} else {
	    clearInterval(hashbangInterval);
	    hashbangInterval=setInterval(function() {
		var serialized=JSON.stringify(window.hashbang);
		if (lastSerialized == serialized) return; // same
		// log("Updating hash... last: " + lastSerialized + ", current: " + serialized);
		lastSerialized=serialized;
		updateHash();
	    }, 500);
	}
	return obj;
    }

    function updateHash() { // Delay because when extensive window.hashbang modification the handler is called too often
	clearTimeout(updateTimeout);
	updateTimeout=setTimeout(function() {
	    var hash=window.hashbangSerialize(window.hashbang);
	    setHash(hash);
	    observe(window.hashbang);
	    trigger('hashbang-serialize');
	}, 50);
    }

    window.hashbangSerialize=function(what) {
	function serialize(keys, val) {
	    // log('Serializing', keys, val);
	    var ret=[], i;

	    switch (val instanceof Array ? 'array' : typeof val) {
	    case 'boolean':
	    case 'number':
	    case 'string':
		var key=(keys.shift() || 'nokey') + (keys.length && '[' + keys.join('][') + ']' || '');
		ret.push(key + '=' + encodeURIComponent(val));
		break;
	    case 'array':
		for (i=0; i < val.length; i++) {
		    keys.push(typeof val[i] == 'object' ? i : '');
		    ret=ret.concat(serialize(keys.concat([]), val[i]));
		    keys.pop();
		}
		break;
	    case 'object':
		for (i in val) {
		    if (i == '#path') continue;
		    keys.push(encodeURIComponent(i));
		    ret=ret.concat(serialize(keys.concat([]), val[i]));
		    keys.pop();
		}
		break;
	    default:
		log("The value type '" + (typeof val) +"' is not supported.");
	    }
	    return ret;
	}

	var path=(what['#path'] || '');
	var hash=serialize([], what).join('&');
	return hash || path ? window.hashbangSeparator + path + (path && hash ? '?' : '') + hash : '#';
    };

    function toArray(obj) {
	var i;
	if (typeof obj == 'object') {
	    var hb=obj.__isArray__ || 0;
	    delete obj.__isArray__;
	    var arr=[];
	    for (i in obj) {
		obj[i]=toArray(obj[i]);
		arr.push(obj[i]);
	    }
	    if (arr.length == hb) { // convert to plain array
		var seq=true;
		for (i=0; i < hb; i++) { // make sure the numbers are sequential
		    if (typeof obj[i] == 'undefined') return obj; // not an array
		}
		obj=arr;
	    }
	}
	return obj;
    }

    function updateObject() {
	if (getHash() == ourHash) {
	    return; // triggered by our updateHash()
	}
	var fireEvent='hashbang' in window ? 'hashbang-parse' : 'hashbang-init';
	var hash=getHash();
	ourHash=hash;
	hasHashbangFormat=isHashbang(hash);
	if (!hasHashbangFormat) fireEvent='hashbang-unparsable';
	var obj=window.hashbangParse(hash);

	lock=true;
	window.hashbang=obj;
	lastSerialized=JSON.stringify(window.hashbang);
	lock=false;

	observe(window.hashbang);
	log("Object updated (" + fireEvent + "): " + JSON.stringify(obj));
	trigger(fireEvent);
    }

    function isHashbang(hash) {
	return (hash.substr(0, window.hashbangSeparator.length) == window.hashbangSeparator && hash.substr(2));
    }

    window.hashbangParse=function (hash) {
	var obj={};

	if (isHashbang(hash)) {
	    var pointer;
	    var pathRE=/^(\/[\/a-z0-9. %_~!$'()*+,;:@-]*)(\?|$)/i; // no '&=' even though path part allows it
	    hash=hash.substr(window.hashbangSeparator.length); // remove '#!'
	    if (hash.match(pathRE)) {
		obj["#path"]=hash.match(pathRE)[1];
		hash=hash.replace(pathRE, '');
	    }
	    var params=hash ? hash.split('&') : [];
	    for (var i=0; i < params.length; i++) {
		var param=params[i].split('=');
		var key=param.shift().replace(/]$/, '');
		var val=decodeURIComponent(param.join('='));

		var keys=key.replace(/\]\[/g, '[').split('[');
		pointer=obj;
		while (keys.length) {
		    var prop=decodeURIComponent(keys.shift()) || pointer.__isArray__;
		    if ((prop + '').match(/^[0-9]+$/) && typeof pointer[prop] == 'undefined') {
			pointer.__isArray__++; // hint possible array
		    }
		    if (keys.length) {
			if (typeof pointer[prop] != 'object') {
			    pointer[prop]={"__isArray__": 0};
			}
			pointer=pointer[prop];
		    } else {
			pointer[prop]=val;
		    }
		}
	    }
	    obj=toArray(obj);
	}

	return obj;
    };

    // First run
    updateObject(); // before Object.observe

    observe(window.hashbang);
    if (typeof Object.observe == 'function' && typeof window.__defineSetter__ == 'function') {
	log("Watching window.hashbang");
	var watchValue=window.hashbang;
	window.__defineGetter__('hashbang', function() {return watchValue;});
	window.__defineSetter__('hashbang', function(val) {watchValue=val; !lock && updateHash(); return watchValue;});
    }

    // Observe Hash
    if ("onhashchange" in window) {
	listen(window, "hashchange", updateObject);
	listen(window, "onhashchange", updateObject); // ie8
	// window.onhashchange=updateObject; // ie8
    } else {
	throw ("Hashbang: The event hashchange is not supported!");
    }
})();
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

// for ES6 stuff like startsWith() that Safari doesn't handle
// and babel doesn't do by default
// Note we use this, as well as the babel transform-runtime plugin
// since transform-runtime does not cover instance methods
// such as "foobar".includes("foo") which bits of our library
// code use, but the babel transform-runtime plugin allows the
// regenerator runtime to be injected early enough in the process
// (it can't be here as it's too late: the alternative is to put
// the babel-polyfill as the first 'entry' in the webpack config).
// https://babeljs.io/docs/plugins/transform-runtime/
require('babel-polyfill');

// Require common CSS here; this will make webpack process it into bundle.css.
// Our own CSS (which is themed) is imported via separate webpack entry points
// in webpack.config.js
require('gemini-scrollbar/gemini-scrollbar.css');
require('gfm.css/gfm.css');
require('highlight.js/styles/github.css');
require('draft-js/dist/Draft.css');

const rageshake = require("./rageshake");
rageshake.init().then(() => {
    console.log("Initialised rageshake: See https://bugs.chromium.org/p/chromium/issues/detail?id=583193 to fix line numbers on Chrome.");
    rageshake.cleanup();
}, (err) => {
    console.error("Failed to initialise rageshake: " + err);
});

 // add React and ReactPerf to the global namespace, to make them easier to
 // access via the console
global.React = require("react");
if (process.env.NODE_ENV !== 'production') {
    global.Perf = require("react-addons-perf");
}

var RunModernizrTests = require("./modernizr"); // this side-effects a global
var ReactDOM = require("react-dom");
var sdk = require("matrix-react-sdk");
var PlatformPeg = require("matrix-react-sdk/lib/PlatformPeg");
sdk.loadSkin(require('../component-index'));
var VectorConferenceHandler = require('../VectorConferenceHandler');
var UpdateChecker = require("./updater");
var q = require('q');
var request = require('browser-request');

import url from 'url';

import {parseQs, parseQsFromFragment} from './url_utils';
import Platform from './platform';

import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';

var lastLocationHashSet = null;

var CallHandler = require("matrix-react-sdk/lib/CallHandler");
CallHandler.setConferenceHandler(VectorConferenceHandler);

MatrixClientPeg.setIndexedDbWorkerScript(window.vector_indexeddb_worker_script);

function checkBrowserFeatures(featureList) {
    if (!window.Modernizr) {
        console.error("Cannot check features - Modernizr global is missing.");
        return false;
    }
    var featureComplete = true;
    for (var i = 0; i < featureList.length; i++) {
        if (window.Modernizr[featureList[i]] === undefined) {
            console.error(
                "Looked for feature '%s' but Modernizr has no results for this. " +
                "Has it been configured correctly?", featureList[i]
            );
            return false;
        }
        if (window.Modernizr[featureList[i]] === false) {
            console.error("Browser missing feature: '%s'", featureList[i]);
            // toggle flag rather than return early so we log all missing features
            // rather than just the first.
            featureComplete = false;
        }
    }
    return featureComplete;
}

var validBrowser = checkBrowserFeatures([
    "displaytable", "flexbox", "es5object", "es5function", "localstorage",
    "objectfit"
]);

// Parse the given window.location and return parameters that can be used when calling
// MatrixChat.showScreen(screen, params)
function getScreenFromLocation(location) {
    const fragparts = parseQsFromFragment(location);
    return {
        screen: fragparts.location.substring(1),
        params: fragparts.params,
    }
}

// Here, we do some crude URL analysis to allow
// deep-linking.
function routeUrl(location) {
    if (!window.matrixChat) return;

    console.log("Routing URL ", location.href);
    const s = getScreenFromLocation(location);
    window.matrixChat.showScreen(s.screen, s.params);
}

function onHashChange(ev) {
    if (decodeURIComponent(window.location.hash) == lastLocationHashSet) {
        // we just set this: no need to route it!
        return;
    }
    routeUrl(window.location);
}

// This will be called whenever the SDK changes screens,
// so a web page can update the URL bar appropriately.
var onNewScreen = function(screen) {
    console.log("newscreen "+screen);
    var hash = '#/' + screen;
    lastLocationHashSet = hash;
    window.location.hash = hash;
}

// We use this to work out what URL the SDK should
// pass through when registering to allow the user to
// click back to the client having registered.
// It's up to us to recognise if we're loaded with
// this URL and tell MatrixClient to resume registration.
//
// If we're in electron, we should never pass through a file:// URL otherwise
// the identity server will try to 302 the browser to it, which breaks horribly.
// so in that instance, hardcode to use riot.im/app for now instead.
var makeRegistrationUrl = function(params) {
    let url;
    if (window.location.protocol === "file:") {
        url = 'https://riot.im/app/#/register';
    } else {
        url = (
            window.location.protocol + '//' +
            window.location.host +
            window.location.pathname +
            '#/register'
        );
    }

    const keys = Object.keys(params);
    for (let i = 0; i < keys.length; ++i) {
        if (i == 0) {
            url += '?';
        } else {
            url += '&';
        }
        const k = keys[i];
        url += k + '=' + encodeURIComponent(params[k]);
    }
    return url;
}

window.addEventListener('hashchange', onHashChange);

function getConfig() {
    let deferred = q.defer();

    request(
        { method: "GET", url: "config.json" },
        (err, response, body) => {
            if (err || response.status < 200 || response.status >= 300) {
                // Lack of a config isn't an error, we should
                // just use the defaults.
                // Also treat a blank config as no config, assuming
                // the status code is 0, because we don't get 404s
                // from file: URIs so this is the only way we can
                // not fail if the file doesn't exist when loading
                // from a file:// URI.
                if (response) {
                    if (response.status == 404 || (response.status == 0 && body == '')) {
                        deferred.resolve({});
                    }
                }
                deferred.reject({err: err, response: response});
                return;
            }

            // We parse the JSON ourselves rather than use the JSON
            // parameter, since this throws a parse error on empty
            // which breaks if there's no config.json and we're
            // loading from the filesystem (see above).
            deferred.resolve(JSON.parse(body));
        }
    );

    return deferred.promise;
}

function onLoadCompleted() {
    // if we did a token login, we're now left with the token, hs and is
    // url as query params in the url; a little nasty but let's redirect to
    // clear them.
    if (window.location.search) {
        var parsedUrl = url.parse(window.location.href);
        parsedUrl.search = "";
        var formatted = url.format(parsedUrl);
        console.log("Redirecting to " + formatted + " to drop loginToken " +
                    "from queryparams");
        window.location.href = formatted;
    }
}

async function loadApp() {
    const fragparts = parseQsFromFragment(window.location);
    const params = parseQs(window.location);

    // set the platform for react sdk (our Platform object automatically picks the right one)
    PlatformPeg.set(new Platform());

    // don't try to redirect to the native apps if we're
    // verifying a 3pid
    const preventRedirect = Boolean(fragparts.params.client_secret);

    if (!preventRedirect) {
        if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
            if (confirm("Riot is not supported on mobile web. Install the app?")) {
                window.location = "https://itunes.apple.com/us/app/vector.im/id1083446067";
                return;
            }
        }
        else if (/Android/.test(navigator.userAgent)) {
            if (confirm("Riot is not supported on mobile web. Install the app?")) {
                window.location = "https://play.google.com/store/apps/details?id=im.vector.alpha";
                return;
            }
        }
    }

    let configJson;
    let configError;
    try {
        configJson = await getConfig();
    } catch (e) {
        configError = e;
    }

    if (window.localStorage && window.localStorage.getItem('mx_accepts_unsupported_browser')) {
        console.log('User has previously accepted risks in using an unsupported browser');
        validBrowser = true;
    }

    console.log("Vector starting at "+window.location);
    if (configError) {
        window.matrixChat = ReactDOM.render(<div className="error">
            Unable to load config file: please refresh the page to try again.
        </div>, document.getElementById('matrixchat'));
    } else if (validBrowser) {
        UpdateChecker.start();

        const MatrixChat = sdk.getComponent('structures.MatrixChat');
        window.matrixChat = ReactDOM.render(
            <MatrixChat
                onNewScreen={onNewScreen}
                makeRegistrationUrl={makeRegistrationUrl}
                ConferenceHandler={VectorConferenceHandler}
                config={configJson}
                realQueryParams={params}
                startingFragmentQueryParams={fragparts.params}
                enableGuest={true}
                onLoadCompleted={onLoadCompleted}
                initialScreenAfterLogin={getScreenFromLocation(window.location)}
                defaultDeviceDisplayName={PlatformPeg.get().getDefaultDeviceDisplayName()}
            />,
            document.getElementById('matrixchat')
        );
    }
    else {
        console.error("Browser is missing required features.");
        // take to a different landing page to AWOOOOOGA at the user
        var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
        window.matrixChat = ReactDOM.render(
            <CompatibilityPage onAccept={function() {
                if (window.localStorage) window.localStorage.setItem('mx_accepts_unsupported_browser', true);
                validBrowser = true;
                console.log("User accepts the compatibility risks.");
                loadApp();
            }} />,
            document.getElementById('matrixchat')
        );
    }
}

loadApp();
<commit_msg>require indexeddb & webworkers in modernizr<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

// for ES6 stuff like startsWith() that Safari doesn't handle
// and babel doesn't do by default
// Note we use this, as well as the babel transform-runtime plugin
// since transform-runtime does not cover instance methods
// such as "foobar".includes("foo") which bits of our library
// code use, but the babel transform-runtime plugin allows the
// regenerator runtime to be injected early enough in the process
// (it can't be here as it's too late: the alternative is to put
// the babel-polyfill as the first 'entry' in the webpack config).
// https://babeljs.io/docs/plugins/transform-runtime/
require('babel-polyfill');

// Require common CSS here; this will make webpack process it into bundle.css.
// Our own CSS (which is themed) is imported via separate webpack entry points
// in webpack.config.js
require('gemini-scrollbar/gemini-scrollbar.css');
require('gfm.css/gfm.css');
require('highlight.js/styles/github.css');
require('draft-js/dist/Draft.css');

const rageshake = require("./rageshake");
rageshake.init().then(() => {
    console.log("Initialised rageshake: See https://bugs.chromium.org/p/chromium/issues/detail?id=583193 to fix line numbers on Chrome.");
    rageshake.cleanup();
}, (err) => {
    console.error("Failed to initialise rageshake: " + err);
});

 // add React and ReactPerf to the global namespace, to make them easier to
 // access via the console
global.React = require("react");
if (process.env.NODE_ENV !== 'production') {
    global.Perf = require("react-addons-perf");
}

var RunModernizrTests = require("./modernizr"); // this side-effects a global
var ReactDOM = require("react-dom");
var sdk = require("matrix-react-sdk");
var PlatformPeg = require("matrix-react-sdk/lib/PlatformPeg");
sdk.loadSkin(require('../component-index'));
var VectorConferenceHandler = require('../VectorConferenceHandler');
var UpdateChecker = require("./updater");
var q = require('q');
var request = require('browser-request');

import url from 'url';

import {parseQs, parseQsFromFragment} from './url_utils';
import Platform from './platform';

import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';

var lastLocationHashSet = null;

var CallHandler = require("matrix-react-sdk/lib/CallHandler");
CallHandler.setConferenceHandler(VectorConferenceHandler);

MatrixClientPeg.setIndexedDbWorkerScript(window.vector_indexeddb_worker_script);

function checkBrowserFeatures(featureList) {
    if (!window.Modernizr) {
        console.error("Cannot check features - Modernizr global is missing.");
        return false;
    }
    var featureComplete = true;
    for (var i = 0; i < featureList.length; i++) {
        if (window.Modernizr[featureList[i]] === undefined) {
            console.error(
                "Looked for feature '%s' but Modernizr has no results for this. " +
                "Has it been configured correctly?", featureList[i]
            );
            return false;
        }
        if (window.Modernizr[featureList[i]] === false) {
            console.error("Browser missing feature: '%s'", featureList[i]);
            // toggle flag rather than return early so we log all missing features
            // rather than just the first.
            featureComplete = false;
        }
    }
    return featureComplete;
}

var validBrowser = checkBrowserFeatures([
    "displaytable", "flexbox", "es5object", "es5function", "localstorage",
    "objectfit", "indexeddb", "webworkers",
]);

// Parse the given window.location and return parameters that can be used when calling
// MatrixChat.showScreen(screen, params)
function getScreenFromLocation(location) {
    const fragparts = parseQsFromFragment(location);
    return {
        screen: fragparts.location.substring(1),
        params: fragparts.params,
    }
}

// Here, we do some crude URL analysis to allow
// deep-linking.
function routeUrl(location) {
    if (!window.matrixChat) return;

    console.log("Routing URL ", location.href);
    const s = getScreenFromLocation(location);
    window.matrixChat.showScreen(s.screen, s.params);
}

function onHashChange(ev) {
    if (decodeURIComponent(window.location.hash) == lastLocationHashSet) {
        // we just set this: no need to route it!
        return;
    }
    routeUrl(window.location);
}

// This will be called whenever the SDK changes screens,
// so a web page can update the URL bar appropriately.
var onNewScreen = function(screen) {
    console.log("newscreen "+screen);
    var hash = '#/' + screen;
    lastLocationHashSet = hash;
    window.location.hash = hash;
}

// We use this to work out what URL the SDK should
// pass through when registering to allow the user to
// click back to the client having registered.
// It's up to us to recognise if we're loaded with
// this URL and tell MatrixClient to resume registration.
//
// If we're in electron, we should never pass through a file:// URL otherwise
// the identity server will try to 302 the browser to it, which breaks horribly.
// so in that instance, hardcode to use riot.im/app for now instead.
var makeRegistrationUrl = function(params) {
    let url;
    if (window.location.protocol === "file:") {
        url = 'https://riot.im/app/#/register';
    } else {
        url = (
            window.location.protocol + '//' +
            window.location.host +
            window.location.pathname +
            '#/register'
        );
    }

    const keys = Object.keys(params);
    for (let i = 0; i < keys.length; ++i) {
        if (i == 0) {
            url += '?';
        } else {
            url += '&';
        }
        const k = keys[i];
        url += k + '=' + encodeURIComponent(params[k]);
    }
    return url;
}

window.addEventListener('hashchange', onHashChange);

function getConfig() {
    let deferred = q.defer();

    request(
        { method: "GET", url: "config.json" },
        (err, response, body) => {
            if (err || response.status < 200 || response.status >= 300) {
                // Lack of a config isn't an error, we should
                // just use the defaults.
                // Also treat a blank config as no config, assuming
                // the status code is 0, because we don't get 404s
                // from file: URIs so this is the only way we can
                // not fail if the file doesn't exist when loading
                // from a file:// URI.
                if (response) {
                    if (response.status == 404 || (response.status == 0 && body == '')) {
                        deferred.resolve({});
                    }
                }
                deferred.reject({err: err, response: response});
                return;
            }

            // We parse the JSON ourselves rather than use the JSON
            // parameter, since this throws a parse error on empty
            // which breaks if there's no config.json and we're
            // loading from the filesystem (see above).
            deferred.resolve(JSON.parse(body));
        }
    );

    return deferred.promise;
}

function onLoadCompleted() {
    // if we did a token login, we're now left with the token, hs and is
    // url as query params in the url; a little nasty but let's redirect to
    // clear them.
    if (window.location.search) {
        var parsedUrl = url.parse(window.location.href);
        parsedUrl.search = "";
        var formatted = url.format(parsedUrl);
        console.log("Redirecting to " + formatted + " to drop loginToken " +
                    "from queryparams");
        window.location.href = formatted;
    }
}

async function loadApp() {
    const fragparts = parseQsFromFragment(window.location);
    const params = parseQs(window.location);

    // set the platform for react sdk (our Platform object automatically picks the right one)
    PlatformPeg.set(new Platform());

    // don't try to redirect to the native apps if we're
    // verifying a 3pid
    const preventRedirect = Boolean(fragparts.params.client_secret);

    if (!preventRedirect) {
        if (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) {
            if (confirm("Riot is not supported on mobile web. Install the app?")) {
                window.location = "https://itunes.apple.com/us/app/vector.im/id1083446067";
                return;
            }
        }
        else if (/Android/.test(navigator.userAgent)) {
            if (confirm("Riot is not supported on mobile web. Install the app?")) {
                window.location = "https://play.google.com/store/apps/details?id=im.vector.alpha";
                return;
            }
        }
    }

    let configJson;
    let configError;
    try {
        configJson = await getConfig();
    } catch (e) {
        configError = e;
    }

    if (window.localStorage && window.localStorage.getItem('mx_accepts_unsupported_browser')) {
        console.log('User has previously accepted risks in using an unsupported browser');
        validBrowser = true;
    }

    console.log("Vector starting at "+window.location);
    if (configError) {
        window.matrixChat = ReactDOM.render(<div className="error">
            Unable to load config file: please refresh the page to try again.
        </div>, document.getElementById('matrixchat'));
    } else if (validBrowser) {
        UpdateChecker.start();

        const MatrixChat = sdk.getComponent('structures.MatrixChat');
        window.matrixChat = ReactDOM.render(
            <MatrixChat
                onNewScreen={onNewScreen}
                makeRegistrationUrl={makeRegistrationUrl}
                ConferenceHandler={VectorConferenceHandler}
                config={configJson}
                realQueryParams={params}
                startingFragmentQueryParams={fragparts.params}
                enableGuest={true}
                onLoadCompleted={onLoadCompleted}
                initialScreenAfterLogin={getScreenFromLocation(window.location)}
                defaultDeviceDisplayName={PlatformPeg.get().getDefaultDeviceDisplayName()}
            />,
            document.getElementById('matrixchat')
        );
    }
    else {
        console.error("Browser is missing required features.");
        // take to a different landing page to AWOOOOOGA at the user
        var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
        window.matrixChat = ReactDOM.render(
            <CompatibilityPage onAccept={function() {
                if (window.localStorage) window.localStorage.setItem('mx_accepts_unsupported_browser', true);
                validBrowser = true;
                console.log("User accepts the compatibility risks.");
                loadApp();
            }} />,
            document.getElementById('matrixchat')
        );
    }
}

loadApp();
<|endoftext|>


<commit_before>'use strict';
var childProcess = require('child_process');
var path = require('path');
var os = require('os');

/**
 * Streams the video to disk using rtmpdump.
 * @param {string} rtmpUrl
 * @param {string} inputPath
 * @param {string} swfUrl
 * @param {string} outputPath
 * @param {function(Error)} done
 */
module.exports = function(rtmpUrl, inputPath, swfUrl, outputPath, done) {
  childProcess.exec(_command() + ' ' +
    '-r "' + rtmpUrl + '" ' +
    '-y "' + inputPath + '" ' +
    '-W "' + swfUrl + '" ' +
    '-o "' + outputPath + '"', {
    maxBuffer: Infinity
  }, done);
};

/**
 * Determines the command for the operating system.
 * @returns {string}
 */
function _command() {
  if (os.platform() !== 'win32') return 'rtmpdump';
  return path.join(__dirname, '../../bin/rtmpdump.exe');
}
<commit_msg>Normalize streaming implementation.<commit_after>'use strict';
var childProcess = require('child_process');
var path = require('path');
var os = require('os');

/**
 * Streams the video to disk.
 * @param {string} rtmpUrl
 * @param {string} rtmpInputPath
 * @param {string} swfUrl
 * @param {string} filePath
 * @param {function(Error)} done
 */
module.exports = function(rtmpUrl, rtmpInputPath, swfUrl, filePath, done) {
  childProcess.exec(_command() + ' ' +
    '-r "' + rtmpUrl + '" ' +
    '-y "' + rtmpInputPath + '" ' +
    '-W "' + swfUrl + '" ' +
    '-o "' + filePath + '"', {
    maxBuffer: Infinity
  }, done);
};

/**
 * Determines the command for the operating system.
 * @returns {string}
 */
function _command() {
  if (os.platform() !== 'win32') return 'rtmpdump';
  return path.join(__dirname, '../../bin/rtmpdump.exe');
}
<|endoftext|>


<commit_before>import {Container} from 'aurelia-dependency-injection';
import {View} from './view';
import {ViewSlot} from './view-slot';
import {ContentSelector} from './content-selector';
import {ViewResources} from './view-resources';
import {BehaviorInstruction, TargetInstruction} from './instructions';
import {DOM} from 'aurelia-pal';

let providerResolverInstance = new (class ProviderResolver {
  get(container, key) {
    let id = key.__providerId__;
    return id in container ? container[id] : (container[id] = container.invoke(key));
  }
});

function elementContainerGet(key) {
  if (key === DOM.Element) {
    return this.element;
  }

  if (key === BoundViewFactory) {
    if (this.boundViewFactory) {
      return this.boundViewFactory;
    }

    let factory = this.instruction.viewFactory;
    let partReplacements = this.partReplacements;

    if (partReplacements) {
      factory = partReplacements[factory.part] || factory;
    }

    this.boundViewFactory = new BoundViewFactory(this, factory, this.bindingContext, partReplacements);
    return this.boundViewFactory;
  }

  if (key === ViewSlot) {
    if (this.viewSlot === undefined) {
      this.viewSlot = new ViewSlot(this.element, this.instruction.anchorIsContainer, this.bindingContext);
      this.children.push(this.viewSlot);
    }

    return this.viewSlot;
  }

  if (key === ViewResources) {
    return this.viewResources;
  }

  if (key === TargetInstruction) {
    return this.instruction;
  }

  return this.superGet(key);
}

function createElementContainer(parent, element, instruction, bindingContext, children, partReplacements, resources) {
  let container = parent.createChild();
  let providers;
  let i;

  container.element = element;
  container.instruction = instruction;
  container.bindingContext = bindingContext;
  container.children = children;
  container.viewResources = resources;
  container.partReplacements = partReplacements;

  providers = instruction.providers;
  i = providers.length;

  while (i--) {
    container.resolvers.set(providers[i], providerResolverInstance);
  }

  container.superGet = container.get;
  container.get = elementContainerGet;

  return container;
}

function makeElementIntoAnchor(element, elementInstruction) {
  let anchor = DOM.createComment('anchor');

  if (elementInstruction) {
    anchor.hasAttribute = function(name) { return element.hasAttribute(name); };
    anchor.getAttribute = function(name) { return element.getAttribute(name); };
    anchor.setAttribute = function(name, value) { element.setAttribute(name, value); };
  }

  DOM.replaceNode(anchor, element);

  return anchor;
}

function applyInstructions(containers, bindingContext, element, instruction,
  behaviors, bindings, children, contentSelectors, partReplacements, resources) {
  let behaviorInstructions = instruction.behaviorInstructions;
  let expressions = instruction.expressions;
  let elementContainer;
  let i;
  let ii;
  let current;
  let instance;

  if (instruction.contentExpression) {
    bindings.push(instruction.contentExpression.createBinding(element.nextSibling));
    element.parentNode.removeChild(element);
    return;
  }

  if (instruction.contentSelector) {
    let commentAnchor = DOM.createComment('anchor');
    DOM.replaceNode(commentAnchor, element);
    contentSelectors.push(new ContentSelector(commentAnchor, instruction.selector));
    return;
  }

  if (behaviorInstructions.length) {
    if (!instruction.anchorIsContainer) {
      element = makeElementIntoAnchor(element, instruction.elementInstruction);
    }

    containers[instruction.injectorId] = elementContainer =
      createElementContainer(
        containers[instruction.parentInjectorId],
        element,
        instruction,
        bindingContext,
        children,
        partReplacements,
        resources
        );

    for (i = 0, ii = behaviorInstructions.length; i < ii; ++i) {
      current = behaviorInstructions[i];
      instance = current.type.create(elementContainer, current, element, bindings, current.partReplacements);

      if (instance.contentView) {
        children.push(instance.contentView);
      }

      behaviors.push(instance);
    }
  }

  for (i = 0, ii = expressions.length; i < ii; ++i) {
    bindings.push(expressions[i].createBinding(element));
  }
}

function styleStringToObject(style, target) {
  let attributes = style.split(';');
  let firstIndexOfColon;
  let i;
  let current;
  let key;
  let value;

  target = target || {};

  for (i = 0; i < attributes.length; i++) {
    current = attributes[i];
    firstIndexOfColon = current.indexOf(':');
    key = current.substring(0, firstIndexOfColon).trim();
    value = current.substring(firstIndexOfColon + 1).trim();
    target[key] = value;
  }

  return target;
}

function styleObjectToString(obj) {
  let result = '';

  for (let key in obj) {
    result += key + ':' + obj[key] + ';';
  }

  return result;
}

function applySurrogateInstruction(container, element, instruction, behaviors, bindings, children) {
  let behaviorInstructions = instruction.behaviorInstructions;
  let expressions = instruction.expressions;
  let providers = instruction.providers;
  let values = instruction.values;
  let i;
  let ii;
  let current;
  let instance;
  let currentAttributeValue;

  i = providers.length;
  while (i--) {
    container.resolvers.set(providers[i], providerResolverInstance);
  }

  //apply surrogate attributes
  for (let key in values) {
    currentAttributeValue = element.getAttribute(key);

    if (currentAttributeValue) {
      if (key === 'class') {
        //merge the surrogate classes
        element.setAttribute('class', currentAttributeValue + ' ' + values[key]);
      } else if (key === 'style') {
        //merge the surrogate styles
        let styleObject = styleStringToObject(values[key]);
        styleStringToObject(currentAttributeValue, styleObject);
        element.setAttribute('style', styleObjectToString(styleObject));
      }

      //otherwise, do not overwrite the consumer's attribute
    } else {
      //copy the surrogate attribute
      element.setAttribute(key, values[key]);
    }
  }

  //apply surrogate behaviors
  if (behaviorInstructions.length) {
    for (i = 0, ii = behaviorInstructions.length; i < ii; ++i) {
      current = behaviorInstructions[i];
      instance = current.type.create(container, current, element, bindings, current.partReplacements);

      if (instance.contentView) {
        children.push(instance.contentView);
      }

      behaviors.push(instance);
    }
  }

  //apply surrogate bindings
  for (i = 0, ii = expressions.length; i < ii; ++i) {
    bindings.push(expressions[i].createBinding(element));
  }
}

export class BoundViewFactory {
  constructor(parentContainer: Container, viewFactory: ViewFactory, bindingContext: Object, partReplacements?: Object) {
    this.parentContainer = parentContainer;
    this.viewFactory = viewFactory;
    this.bindingContext = bindingContext;
    this.factoryCreateInstruction = { partReplacements: partReplacements };
  }

  create(bindingContext?: Object): View {
    let childContainer = this.parentContainer.createChild();
    let context = bindingContext || this.bindingContext;

    this.factoryCreateInstruction.systemControlled = !bindingContext;

    return this.viewFactory.create(childContainer, context, this.factoryCreateInstruction);
  }

  get isCaching() {
    return this.viewFactory.isCaching;
  }

  setCacheSize(size: number | string, doNotOverrideIfAlreadySet: boolean): void {
    this.viewFactory.setCacheSize(size, doNotOverrideIfAlreadySet);
  }

  getCachedView(): View {
    return this.viewFactory.getCachedView();
  }

  returnViewToCache(view: View): void {
    this.viewFactory.returnViewToCache(view);
  }
}

export class ViewFactory {
  constructor(template: DocumentFragment, instructions: Object, resources: ViewResources) {
    this.template = template;
    this.instructions = instructions;
    this.resources = resources;
    this.cacheSize = -1;
    this.cache = null;
    this.isCaching = false;
  }

  setCacheSize(size: number | string, doNotOverrideIfAlreadySet: boolean): void {
    if (size) {
      if (size === '*') {
        size = Number.MAX_VALUE;
      } else if (typeof size === 'string') {
        size = parseInt(size, 10);
      }
    }

    if (this.cacheSize === -1 || !doNotOverrideIfAlreadySet) {
      this.cacheSize = size;
    }

    if (this.cacheSize > 0) {
      this.cache = [];
    } else {
      this.cache = null;
    }

    this.isCaching = this.cacheSize > 0;
  }

  getCachedView(): View {
    return this.cache !== null ? (this.cache.pop() || null) : null;
  }

  returnViewToCache(view: View): void {
    if (view.isAttached) {
      view.detached();
    }

    if (view.isBound) {
      view.unbind();
    }

    if (this.cache !== null && this.cache.length < this.cacheSize) {
      view.fromCache = true;
      this.cache.push(view);
    }
  }

  create(container: Container, bindingContext?: Object, createInstruction?: ViewCreateInstruction, element?: Element): View {
    createInstruction = createInstruction || BehaviorInstruction.normal;
    element = element || null;

    let cachedView = this.getCachedView();
    if (cachedView !== null) {
      if (!createInstruction.suppressBind) {
        cachedView.bind(bindingContext);
      }

      return cachedView;
    }

    let fragment = createInstruction.enhance ? this.template : this.template.cloneNode(true);
    let instructables = fragment.querySelectorAll('.au-target');
    let instructions = this.instructions;
    let resources = this.resources;
    let behaviors = [];
    let bindings = [];
    let children = [];
    let contentSelectors = [];
    let containers = { root: container };
    let partReplacements = createInstruction.partReplacements;
    let i;
    let ii;
    let view;
    let instructable;
    let instruction;

    this.resources.onBeforeCreate(this, container, fragment, createInstruction, bindingContext);

    if (element !== null && this.surrogateInstruction !== null) {
      applySurrogateInstruction(container, element, this.surrogateInstruction, behaviors, bindings, children);
    }

    for (i = 0, ii = instructables.length; i < ii; ++i) {
      instructable = instructables[i];
      instruction = instructions[instructable.getAttribute('au-target-id')];

      applyInstructions(containers, bindingContext, instructable,
        instruction, behaviors, bindings, children, contentSelectors, partReplacements, resources);
    }

    view = new View(this, container, fragment, behaviors, bindings, children, createInstruction.systemControlled, contentSelectors);

    //if iniated by an element behavior, let the behavior trigger this callback once it's done creating the element
    if (!createInstruction.initiatedByBehavior) {
      view.created();
    }

    this.resources.onAfterCreate(view);

    //if the view creation is part of a larger creation, wait to bind until the root view initiates binding
    if (!createInstruction.suppressBind) {
      view.bind(bindingContext);
    }

    return view;
  }
}
<commit_msg>fix(view-factory): update to latest container api<commit_after>import {Container, resolver} from 'aurelia-dependency-injection';
import {View} from './view';
import {ViewSlot} from './view-slot';
import {ContentSelector} from './content-selector';
import {ViewResources} from './view-resources';
import {BehaviorInstruction, TargetInstruction} from './instructions';
import {DOM} from 'aurelia-pal';

@resolver
class ProviderResolver {
  get(container, key) {
    let id = key.__providerId__;
    return id in container ? container[id] : (container[id] = container.invoke(key));
  }
}

let providerResolverInstance = new ProviderResolver();

function elementContainerGet(key) {
  if (key === DOM.Element) {
    return this.element;
  }

  if (key === BoundViewFactory) {
    if (this.boundViewFactory) {
      return this.boundViewFactory;
    }

    let factory = this.instruction.viewFactory;
    let partReplacements = this.partReplacements;

    if (partReplacements) {
      factory = partReplacements[factory.part] || factory;
    }

    this.boundViewFactory = new BoundViewFactory(this, factory, this.bindingContext, partReplacements);
    return this.boundViewFactory;
  }

  if (key === ViewSlot) {
    if (this.viewSlot === undefined) {
      this.viewSlot = new ViewSlot(this.element, this.instruction.anchorIsContainer, this.bindingContext);
      this.children.push(this.viewSlot);
    }

    return this.viewSlot;
  }

  if (key === ViewResources) {
    return this.viewResources;
  }

  if (key === TargetInstruction) {
    return this.instruction;
  }

  return this.superGet(key);
}

function createElementContainer(parent, element, instruction, bindingContext, children, partReplacements, resources) {
  let container = parent.createChild();
  let providers;
  let i;

  container.element = element;
  container.instruction = instruction;
  container.bindingContext = bindingContext;
  container.children = children;
  container.viewResources = resources;
  container.partReplacements = partReplacements;

  providers = instruction.providers;
  i = providers.length;

  while (i--) {
    container._resolvers.set(providers[i], providerResolverInstance);
  }

  container.superGet = container.get;
  container.get = elementContainerGet;

  return container;
}

function makeElementIntoAnchor(element, elementInstruction) {
  let anchor = DOM.createComment('anchor');

  if (elementInstruction) {
    anchor.hasAttribute = function(name) { return element.hasAttribute(name); };
    anchor.getAttribute = function(name) { return element.getAttribute(name); };
    anchor.setAttribute = function(name, value) { element.setAttribute(name, value); };
  }

  DOM.replaceNode(anchor, element);

  return anchor;
}

function applyInstructions(containers, bindingContext, element, instruction,
  behaviors, bindings, children, contentSelectors, partReplacements, resources) {
  let behaviorInstructions = instruction.behaviorInstructions;
  let expressions = instruction.expressions;
  let elementContainer;
  let i;
  let ii;
  let current;
  let instance;

  if (instruction.contentExpression) {
    bindings.push(instruction.contentExpression.createBinding(element.nextSibling));
    element.parentNode.removeChild(element);
    return;
  }

  if (instruction.contentSelector) {
    let commentAnchor = DOM.createComment('anchor');
    DOM.replaceNode(commentAnchor, element);
    contentSelectors.push(new ContentSelector(commentAnchor, instruction.selector));
    return;
  }

  if (behaviorInstructions.length) {
    if (!instruction.anchorIsContainer) {
      element = makeElementIntoAnchor(element, instruction.elementInstruction);
    }

    containers[instruction.injectorId] = elementContainer =
      createElementContainer(
        containers[instruction.parentInjectorId],
        element,
        instruction,
        bindingContext,
        children,
        partReplacements,
        resources
        );

    for (i = 0, ii = behaviorInstructions.length; i < ii; ++i) {
      current = behaviorInstructions[i];
      instance = current.type.create(elementContainer, current, element, bindings, current.partReplacements);

      if (instance.contentView) {
        children.push(instance.contentView);
      }

      behaviors.push(instance);
    }
  }

  for (i = 0, ii = expressions.length; i < ii; ++i) {
    bindings.push(expressions[i].createBinding(element));
  }
}

function styleStringToObject(style, target) {
  let attributes = style.split(';');
  let firstIndexOfColon;
  let i;
  let current;
  let key;
  let value;

  target = target || {};

  for (i = 0; i < attributes.length; i++) {
    current = attributes[i];
    firstIndexOfColon = current.indexOf(':');
    key = current.substring(0, firstIndexOfColon).trim();
    value = current.substring(firstIndexOfColon + 1).trim();
    target[key] = value;
  }

  return target;
}

function styleObjectToString(obj) {
  let result = '';

  for (let key in obj) {
    result += key + ':' + obj[key] + ';';
  }

  return result;
}

function applySurrogateInstruction(container, element, instruction, behaviors, bindings, children) {
  let behaviorInstructions = instruction.behaviorInstructions;
  let expressions = instruction.expressions;
  let providers = instruction.providers;
  let values = instruction.values;
  let i;
  let ii;
  let current;
  let instance;
  let currentAttributeValue;

  i = providers.length;
  while (i--) {
    container._resolvers.set(providers[i], providerResolverInstance);
  }

  //apply surrogate attributes
  for (let key in values) {
    currentAttributeValue = element.getAttribute(key);

    if (currentAttributeValue) {
      if (key === 'class') {
        //merge the surrogate classes
        element.setAttribute('class', currentAttributeValue + ' ' + values[key]);
      } else if (key === 'style') {
        //merge the surrogate styles
        let styleObject = styleStringToObject(values[key]);
        styleStringToObject(currentAttributeValue, styleObject);
        element.setAttribute('style', styleObjectToString(styleObject));
      }

      //otherwise, do not overwrite the consumer's attribute
    } else {
      //copy the surrogate attribute
      element.setAttribute(key, values[key]);
    }
  }

  //apply surrogate behaviors
  if (behaviorInstructions.length) {
    for (i = 0, ii = behaviorInstructions.length; i < ii; ++i) {
      current = behaviorInstructions[i];
      instance = current.type.create(container, current, element, bindings, current.partReplacements);

      if (instance.contentView) {
        children.push(instance.contentView);
      }

      behaviors.push(instance);
    }
  }

  //apply surrogate bindings
  for (i = 0, ii = expressions.length; i < ii; ++i) {
    bindings.push(expressions[i].createBinding(element));
  }
}

export class BoundViewFactory {
  constructor(parentContainer: Container, viewFactory: ViewFactory, bindingContext: Object, partReplacements?: Object) {
    this.parentContainer = parentContainer;
    this.viewFactory = viewFactory;
    this.bindingContext = bindingContext;
    this.factoryCreateInstruction = { partReplacements: partReplacements };
  }

  create(bindingContext?: Object): View {
    let childContainer = this.parentContainer.createChild();
    let context = bindingContext || this.bindingContext;

    this.factoryCreateInstruction.systemControlled = !bindingContext;

    return this.viewFactory.create(childContainer, context, this.factoryCreateInstruction);
  }

  get isCaching() {
    return this.viewFactory.isCaching;
  }

  setCacheSize(size: number | string, doNotOverrideIfAlreadySet: boolean): void {
    this.viewFactory.setCacheSize(size, doNotOverrideIfAlreadySet);
  }

  getCachedView(): View {
    return this.viewFactory.getCachedView();
  }

  returnViewToCache(view: View): void {
    this.viewFactory.returnViewToCache(view);
  }
}

export class ViewFactory {
  constructor(template: DocumentFragment, instructions: Object, resources: ViewResources) {
    this.template = template;
    this.instructions = instructions;
    this.resources = resources;
    this.cacheSize = -1;
    this.cache = null;
    this.isCaching = false;
  }

  setCacheSize(size: number | string, doNotOverrideIfAlreadySet: boolean): void {
    if (size) {
      if (size === '*') {
        size = Number.MAX_VALUE;
      } else if (typeof size === 'string') {
        size = parseInt(size, 10);
      }
    }

    if (this.cacheSize === -1 || !doNotOverrideIfAlreadySet) {
      this.cacheSize = size;
    }

    if (this.cacheSize > 0) {
      this.cache = [];
    } else {
      this.cache = null;
    }

    this.isCaching = this.cacheSize > 0;
  }

  getCachedView(): View {
    return this.cache !== null ? (this.cache.pop() || null) : null;
  }

  returnViewToCache(view: View): void {
    if (view.isAttached) {
      view.detached();
    }

    if (view.isBound) {
      view.unbind();
    }

    if (this.cache !== null && this.cache.length < this.cacheSize) {
      view.fromCache = true;
      this.cache.push(view);
    }
  }

  create(container: Container, bindingContext?: Object, createInstruction?: ViewCreateInstruction, element?: Element): View {
    createInstruction = createInstruction || BehaviorInstruction.normal;
    element = element || null;

    let cachedView = this.getCachedView();
    if (cachedView !== null) {
      if (!createInstruction.suppressBind) {
        cachedView.bind(bindingContext);
      }

      return cachedView;
    }

    let fragment = createInstruction.enhance ? this.template : this.template.cloneNode(true);
    let instructables = fragment.querySelectorAll('.au-target');
    let instructions = this.instructions;
    let resources = this.resources;
    let behaviors = [];
    let bindings = [];
    let children = [];
    let contentSelectors = [];
    let containers = { root: container };
    let partReplacements = createInstruction.partReplacements;
    let i;
    let ii;
    let view;
    let instructable;
    let instruction;

    this.resources.onBeforeCreate(this, container, fragment, createInstruction, bindingContext);

    if (element !== null && this.surrogateInstruction !== null) {
      applySurrogateInstruction(container, element, this.surrogateInstruction, behaviors, bindings, children);
    }

    for (i = 0, ii = instructables.length; i < ii; ++i) {
      instructable = instructables[i];
      instruction = instructions[instructable.getAttribute('au-target-id')];

      applyInstructions(containers, bindingContext, instructable,
        instruction, behaviors, bindings, children, contentSelectors, partReplacements, resources);
    }

    view = new View(this, container, fragment, behaviors, bindings, children, createInstruction.systemControlled, contentSelectors);

    //if iniated by an element behavior, let the behavior trigger this callback once it's done creating the element
    if (!createInstruction.initiatedByBehavior) {
      view.created();
    }

    this.resources.onAfterCreate(view);

    //if the view creation is part of a larger creation, wait to bind until the root view initiates binding
    if (!createInstruction.suppressBind) {
      view.bind(bindingContext);
    }

    return view;
  }
}
<|endoftext|>


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/**
 * APIs for accessibility VoiceReadout
 * @namespace webOS.voicereadout
 */

webOS.voicereadout = {

	/**
	 * Read alert text when accessibility VoiceReadout enabled.
	 * @param {string} s - String to voice readout.
	 */
	readAlert: function(s) {
		if(webOS && webOS.platform && webOS.platform.watch) {

			var locale, speechRate;

			/**
			* Check VoiceReadOut talkback is enabled or not.
			*
			* @private
			*/
			var checkVoiceReadOut = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"VoiceReadOut"},
					onSuccess: function(inResponse) {
						if (inResponse && inResponse.settings.talkbackEnable) {
							callback();
						}
					},
					onFailure: function(inError) {
						console.error("Failed to get system VoiceReadOut settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current locale info.
			*
			* @private
			*/
			var getLocaleInfo = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"keys": ["localeInfo"]},
					onSuccess: function(inResponse) {
						locale = inResponse.settings.localeInfo.locales.TTS;
						callback();
					},
					onFailure: function(inError) {
						console.error("Failed to get system localeInfo settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current speech rate.
			*
			* @private
			*/
			var getSpeechRate = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"option", "key":"ttsSpeechRate"},
					onSuccess: function(inResponse) {
						speechRate = Number(inResponse.settings.ttsSpeechRate);
						callback();
					},
					onFailure: function(inError) {
						console.error("Failed to get system speechRate settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Read alert message using TTS api.
			*
			* @private
			*/
			var readAlertMessage = function() {
				webOS.service.request("luna://com.lge.service.tts", {
					method: "speak",
					parameters: {"locale":locale, "text":s, "speechRate":speechRate}
				});
			};

			checkVoiceReadOut(function() {
				getLocaleInfo(function() {
					getSpeechRate(readAlertMessage);
				});
			});
		}
	}
};
<commit_msg>ENYO-1126: Accessibility: Add "readAlert()" to enyo<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/**
 * APIs for accessibility VoiceReadout
 * @namespace webOS.voicereadout
 */

webOS.voicereadout = {

	/**
	 * Read alert text when accessibility VoiceReadout enabled.
	 * @param {string} s - String to voice readout.
	 */
	readAlert: function(s) {
		if(webOS && webOS.platform && webOS.platform.watch) {

			var locale, speechRate;

			/**
			* Check VoiceReadOut talkback is enabled or not.
			*
			* @private
			*/
			var checkVoiceReadOut = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"VoiceReadOut"},
					onSuccess: function(inResponse) {
						if (inResponse && inResponse.settings.talkbackEnable) {
							callback();
						}
					},
					onFailure: function(inError) {
						console.error("Failed to get system VoiceReadOut settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current locale info.
			*
			* @private
			*/
			var getLocaleInfo = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"keys": ["localeInfo"]},
					onSuccess: function(inResponse) {
						locale = inResponse.settings.localeInfo.locales.TTS;
						callback();
					},
					onFailure: function(inError) {
						console.error("Failed to get system localeInfo settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Get current speech rate.
			*
			* @private
			*/
			var getSpeechRate = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"category":"option", "key":"ttsSpeechRate"},
					onSuccess: function(inResponse) {
						speechRate = Number(inResponse.settings.ttsSpeechRate);
						callback();
					},
					onFailure: function(inError) {
						console.error("Failed to get system speechRate settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Read alert message using TTS api.
			*
			* @private
			*/
			var readAlertMessage = function() {
				webOS.service.request("luna://com.lge.service.tts", {
					method: "speak",
					parameters: {"locale":locale, "text":s, "speechRate":speechRate}
				});
			};

			checkVoiceReadOut(function() {
				getLocaleInfo(function() {
					getSpeechRate(readAlertMessage);
				});
			});
		} else if (webOS && webOS.platform && webOS.platform.tv) {

			/**
			* Check AudioGuidance is enabled or not.
			*
			* @private
			*/
			var checkAudioGuidance = function(callback) {
				webOS.service.request("luna://com.webos.settingsservice", {
					method: "getSystemSettings",
					parameters: {"keys" : ["audioGuidance"],"category": "option"},
					onSuccess: function(inResponse) {
						if (inResponse && inResponse.settings.audioGuidance === "on") {
							callback();
						}
					},
					onFailure: function(inError) {
						console.error("Failed to get system AudioGuidance settings: " + JSON.stringify(inError));
					}
				});
			};

			/**
			* Read alert message using TTS api.
			*
			* @private
			*/
			var readAlertMessage = function() {
				webOS.service.request("luna://com.webos.service.tts", {
					method: "speak",
					parameters: {"text":s, "clear": true},
					onFailure: function(inError) {
						console.error("Failed to readAlertMessage: " + JSON.stringify(inError));
					}
				});
			};

			checkAudioGuidance(readAlertMessage);
		}
	}
};
<|endoftext|>


<commit_before>import Sortable from "sortablejs";
import { console, camelize } from "./util/helper";

function buildAttribute(object, propName, value) {
  if (value == undefined) {
    return object;
  }
  object = object == null ? {} : object;
  object[propName] = value;
  return object;
}

function removeNode(node) {
  if (node.parentElement !== null) {
    node.parentElement.removeChild(node);
  }
}

function insertNodeAt(fatherNode, node, position) {
  const refNode =
    position === 0
      ? fatherNode.children[0]
      : fatherNode.children[position - 1].nextSibling;
  fatherNode.insertBefore(node, refNode);
}

function computeVmIndex(vnodes, element) {
  return vnodes.map(elt => elt.elm).indexOf(element);
}

function computeIndexes(slots, children, isTransition, footerOffset) {
  if (!slots) {
    return [];
  }

  const elmFromNodes = slots.map(elt => elt.elm);
  const footerIndex = children.length - footerOffset;
  const rawIndexes = [...children].map((elt, idx) =>
    idx >= footerIndex ? elmFromNodes.length : elmFromNodes.indexOf(elt)
  );
  return isTransition ? rawIndexes.filter(ind => ind !== -1) : rawIndexes;
}

function emit(evtName, evtData) {
  this.$nextTick(() => this.$emit(evtName.toLowerCase(), evtData));
}

function delegateAndEmit(evtName) {
  return evtData => {
    if (this.realList !== null) {
      this["onDrag" + evtName](evtData);
    }
    emit.call(this, evtName, evtData);
  };
}

const eventsListened = ["Start", "Add", "Remove", "Update", "End"];
const eventsToEmit = ["Choose", "Sort", "Filter", "Clone"];
const readonlyProperties = ["Move", ...eventsListened, ...eventsToEmit].map(
  evt => "on" + evt
);
var draggingElement = null;

const props = {
  options: Object,
  list: {
    type: Array,
    required: false,
    default: null
  },
  value: {
    type: Array,
    required: false,
    default: null
  },
  noTransitionOnDrag: {
    type: Boolean,
    default: false
  },
  clone: {
    type: Function,
    default: original => {
      return original;
    }
  },
  element: {
    type: String,
    default: "div"
  },
  tag: {
    type: String,
    default: null
  },
  move: {
    type: Function,
    default: null
  },
  componentData: {
    type: Object,
    required: false,
    default: null
  }
};

const draggableComponent = {
  name: "draggable",

  inheritAttrs: false,

  props,

  data() {
    return {
      transitionMode: false,
      noneFunctionalComponentMode: false,
      init: false
    };
  },

  render(h) {
    const slots = this.$slots.default;
    if (slots && slots.length === 1) {
      const child = slots[0];
      if (
        child.componentOptions &&
        ["transition-group", "TransitionGroup"].includes(
          child.componentOptions.tag
        )
      ) {
        this.transitionMode = true;
      }
    }
    let headerOffset = 0;
    let footerOffset = 0;
    let children = slots;
    const { header, footer } = this.$slots;
    if (header) {
      headerOffset = header.length;
      children = children ? [...header, ...children] : [...header];
    }
    if (footer) {
      footerOffset = footer.length;
      children = children ? [...children, ...footer] : [...footer];
    }
    this.headerOffset = headerOffset;
    this.footerOffset = footerOffset;
    var attributes = null;
    const update = (name, value) => {
      attributes = buildAttribute(attributes, name, value);
    };
    const attrs = Object.keys(this.$attrs)
      .filter(key => key === "id" || key.startsWith("data-"))
      .reduce((res, key) => {
        res[key] = this.$attrs[key];
        return res;
      }, {});
    update("attrs", attrs);

    if (this.componentData) {
      const { on, props, attrs } = this.componentData;
      update("on", on);
      update("props", props);
      Object.assign(attributes.attrs, attrs);
    }
    return h(this.getTag(), attributes, children);
  },

  created() {
    if (this.list !== null && this.value !== null) {
      console.error(
        "Value and list props are mutually exclusive! Please set one or another."
      );
    }

    if (this.element !== "div") {
      console.warn(
        "Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"
      );
    }

    if (this.options !== undefined) {
      console.warn(
        "Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props"
      );
    }
  },

  mounted() {
    this.noneFunctionalComponentMode =
      this.getTag().toLowerCase() !== this.$el.nodeName.toLowerCase();
    if (this.noneFunctionalComponentMode && this.transitionMode) {
      throw new Error(
        `Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ${this.getTag()}`
      );
    }
    var optionsAdded = {};
    eventsListened.forEach(elt => {
      optionsAdded["on" + elt] = delegateAndEmit.call(this, elt);
    });

    eventsToEmit.forEach(elt => {
      optionsAdded["on" + elt] = emit.bind(this, elt);
    });

    const attributes = Object.keys(this.$attrs).reduce((res, key) => {
      res[camelize(key)] = this.$attrs[key];
      return res;
    }, {});

    const options = Object.assign({}, this.options, attributes, optionsAdded, {
      onMove: (evt, originalEvent) => {
        return this.onDragMove(evt, originalEvent);
      }
    });
    !("draggable" in options) && (options.draggable = ">*");
    this._sortable = new Sortable(this.rootContainer, options);
    this.computeIndexes();
  },

  beforeDestroy() {
    if (this._sortable !== undefined) this._sortable.destroy();
  },

  computed: {
    rootContainer() {
      return this.transitionMode ? this.$el.children[0] : this.$el;
    },

    realList() {
      return this.list ? this.list : this.value;
    }
  },

  watch: {
    options: {
      handler(newOptionValue) {
        this.updateOptions(newOptionValue);
      },
      deep: true
    },

    $attrs: {
      handler(newOptionValue) {
        this.updateOptions(newOptionValue);
      },
      deep: true
    },

    realList() {
      this.computeIndexes();
    }
  },

  methods: {
    getTag() {
      return this.tag || this.element;
    },

    getOptionGroup() {
      const { options } = this;
      if (!options) {
        return undefined;
      }
      return options.group;
    },

    updateOptions(newOptionValue) {
      for (var property in newOptionValue) {
        const value = camelize(property);
        if (readonlyProperties.indexOf(value) == -1) {
          this._sortable.option(value, newOptionValue[property]);
        }
      }
    },

    getChildrenNodes() {
      if (!this.init) {
        this.noneFunctionalComponentMode =
          this.noneFunctionalComponentMode && this.$children.length == 1;
        this.init = true;
      }

      if (this.noneFunctionalComponentMode) {
        return this.$children[0].$slots.default;
      }
      const rawNodes = this.$slots.default;
      return this.transitionMode ? rawNodes[0].child.$slots.default : rawNodes;
    },

    computeIndexes() {
      this.$nextTick(() => {
        this.visibleIndexes = computeIndexes(
          this.getChildrenNodes(),
          this.rootContainer.children,
          this.transitionMode,
          this.footerOffset
        );
      });
    },

    getUnderlyingVm(htmlElt) {
      const index = computeVmIndex(this.getChildrenNodes() || [], htmlElt);
      if (index === -1) {
        //Edge case during move callback: related element might be
        //an element different from collection
        return null;
      }
      const element = this.realList[index];
      return { index, element };
    },

    getUnderlyingPotencialDraggableComponent({ __vue__ }) {
      if (
        !__vue__ ||
        !__vue__.$options ||
        __vue__.$options._componentTag !== "transition-group"
      ) {
        return __vue__;
      }
      return __vue__.$parent;
    },

    emitChanges(evt) {
      this.$nextTick(() => {
        this.$emit("change", evt);
      });
    },

    alterList(onList) {
      if (this.list) {
        onList(this.list);
        return;
      }
      const newList = [...this.value];
      onList(newList);
      this.$emit("input", newList);
    },

    spliceList() {
      const spliceList = list => list.splice(...arguments);
      this.alterList(spliceList);
    },

    updatePosition(oldIndex, newIndex) {
      const updatePosition = list =>
        list.splice(newIndex, 0, list.splice(oldIndex, 1)[0]);
      this.alterList(updatePosition);
    },

    getRelatedContextFromMoveEvent({ to, related }) {
      const component = this.getUnderlyingPotencialDraggableComponent(to);
      if (!component) {
        return { component };
      }
      const list = component.realList;
      const context = { list, component };
      if (to !== related && list && component.getUnderlyingVm) {
        const destination = component.getUnderlyingVm(related);
        if (destination) {
          return Object.assign(destination, context);
        }
      }
      return context;
    },

    getVmIndex(domIndex) {
      const indexes = this.visibleIndexes;
      const numberIndexes = indexes.length;
      return domIndex > numberIndexes - 1 ? numberIndexes : indexes[domIndex];
    },

    getComponent() {
      return this.$slots.default[0].componentInstance;
    },

    resetTransitionData(index) {
      if (!this.noTransitionOnDrag || !this.transitionMode) {
        return;
      }
      var nodes = this.getChildrenNodes();
      nodes[index].data = null;
      const transitionContainer = this.getComponent();
      transitionContainer.children = [];
      transitionContainer.kept = undefined;
    },

    onDragStart(evt) {
      this.context = this.getUnderlyingVm(evt.item);
      evt.item._underlying_vm_ = this.clone(this.context.element);
      draggingElement = evt.item;
    },

    onDragAdd(evt) {
      const element = evt.item._underlying_vm_;
      if (element === undefined) {
        return;
      }
      removeNode(evt.item);
      const newIndex = this.getVmIndex(evt.newIndex);
      this.spliceList(newIndex, 0, element);
      this.computeIndexes();
      const added = { element, newIndex };
      this.emitChanges({ added });
    },

    onDragRemove(evt) {
      insertNodeAt(this.rootContainer, evt.item, evt.oldIndex);
      if (evt.pullMode === "clone") {
        removeNode(evt.clone);
        return;
      }
      const oldIndex = this.context.index;
      this.spliceList(oldIndex, 1);
      const removed = { element: this.context.element, oldIndex };
      this.resetTransitionData(oldIndex);
      this.emitChanges({ removed });
    },

    onDragUpdate(evt) {
      removeNode(evt.item);
      insertNodeAt(evt.from, evt.item, evt.oldIndex);
      const oldIndex = this.context.index;
      const newIndex = this.getVmIndex(evt.newIndex);
      this.updatePosition(oldIndex, newIndex);
      const moved = { element: this.context.element, oldIndex, newIndex };
      this.emitChanges({ moved });
    },

    updateProperty(evt, propertyName) {
      evt.hasOwnProperty(propertyName) &&
        (evt[propertyName] += this.headerOffset);
    },

    computeFutureIndex(relatedContext, evt) {
      if (!relatedContext.element) {
        return 0;
      }
      const domChildren = [...evt.to.children].filter(
        el => el.style["display"] !== "none"
      );
      const currentDOMIndex = domChildren.indexOf(evt.related);
      const currentIndex = relatedContext.component.getVmIndex(currentDOMIndex);
      const draggedInList = domChildren.indexOf(draggingElement) != -1;
      return draggedInList || !evt.willInsertAfter
        ? currentIndex
        : currentIndex + 1;
    },

    onDragMove(evt, originalEvent) {
      const onMove = this.move;
      if (!onMove || !this.realList) {
        return true;
      }

      const relatedContext = this.getRelatedContextFromMoveEvent(evt);
      const draggedContext = this.context;
      const futureIndex = this.computeFutureIndex(relatedContext, evt);
      Object.assign(draggedContext, { futureIndex });
      const sendEvt = Object.assign({}, evt, {
        relatedContext,
        draggedContext
      });
      return onMove(sendEvt, originalEvent);
    },

    onDragEnd() {
      this.computeIndexes();
      draggingElement = null;
    }
  }
};

if (typeof window !== "undefined" && "Vue" in window) {
  window.Vue.component("draggable", draggableComponent);
}

export default draggableComponent;
<commit_msg>Removing unused function<commit_after>import Sortable from "sortablejs";
import { console, camelize } from "./util/helper";

function buildAttribute(object, propName, value) {
  if (value == undefined) {
    return object;
  }
  object = object == null ? {} : object;
  object[propName] = value;
  return object;
}

function removeNode(node) {
  if (node.parentElement !== null) {
    node.parentElement.removeChild(node);
  }
}

function insertNodeAt(fatherNode, node, position) {
  const refNode =
    position === 0
      ? fatherNode.children[0]
      : fatherNode.children[position - 1].nextSibling;
  fatherNode.insertBefore(node, refNode);
}

function computeVmIndex(vnodes, element) {
  return vnodes.map(elt => elt.elm).indexOf(element);
}

function computeIndexes(slots, children, isTransition, footerOffset) {
  if (!slots) {
    return [];
  }

  const elmFromNodes = slots.map(elt => elt.elm);
  const footerIndex = children.length - footerOffset;
  const rawIndexes = [...children].map((elt, idx) =>
    idx >= footerIndex ? elmFromNodes.length : elmFromNodes.indexOf(elt)
  );
  return isTransition ? rawIndexes.filter(ind => ind !== -1) : rawIndexes;
}

function emit(evtName, evtData) {
  this.$nextTick(() => this.$emit(evtName.toLowerCase(), evtData));
}

function delegateAndEmit(evtName) {
  return evtData => {
    if (this.realList !== null) {
      this["onDrag" + evtName](evtData);
    }
    emit.call(this, evtName, evtData);
  };
}

const eventsListened = ["Start", "Add", "Remove", "Update", "End"];
const eventsToEmit = ["Choose", "Sort", "Filter", "Clone"];
const readonlyProperties = ["Move", ...eventsListened, ...eventsToEmit].map(
  evt => "on" + evt
);
var draggingElement = null;

const props = {
  options: Object,
  list: {
    type: Array,
    required: false,
    default: null
  },
  value: {
    type: Array,
    required: false,
    default: null
  },
  noTransitionOnDrag: {
    type: Boolean,
    default: false
  },
  clone: {
    type: Function,
    default: original => {
      return original;
    }
  },
  element: {
    type: String,
    default: "div"
  },
  tag: {
    type: String,
    default: null
  },
  move: {
    type: Function,
    default: null
  },
  componentData: {
    type: Object,
    required: false,
    default: null
  }
};

const draggableComponent = {
  name: "draggable",

  inheritAttrs: false,

  props,

  data() {
    return {
      transitionMode: false,
      noneFunctionalComponentMode: false,
      init: false
    };
  },

  render(h) {
    const slots = this.$slots.default;
    if (slots && slots.length === 1) {
      const child = slots[0];
      if (
        child.componentOptions &&
        ["transition-group", "TransitionGroup"].includes(
          child.componentOptions.tag
        )
      ) {
        this.transitionMode = true;
      }
    }
    let headerOffset = 0;
    let footerOffset = 0;
    let children = slots;
    const { header, footer } = this.$slots;
    if (header) {
      headerOffset = header.length;
      children = children ? [...header, ...children] : [...header];
    }
    if (footer) {
      footerOffset = footer.length;
      children = children ? [...children, ...footer] : [...footer];
    }
    this.headerOffset = headerOffset;
    this.footerOffset = footerOffset;
    var attributes = null;
    const update = (name, value) => {
      attributes = buildAttribute(attributes, name, value);
    };
    const attrs = Object.keys(this.$attrs)
      .filter(key => key === "id" || key.startsWith("data-"))
      .reduce((res, key) => {
        res[key] = this.$attrs[key];
        return res;
      }, {});
    update("attrs", attrs);

    if (this.componentData) {
      const { on, props, attrs } = this.componentData;
      update("on", on);
      update("props", props);
      Object.assign(attributes.attrs, attrs);
    }
    return h(this.getTag(), attributes, children);
  },

  created() {
    if (this.list !== null && this.value !== null) {
      console.error(
        "Value and list props are mutually exclusive! Please set one or another."
      );
    }

    if (this.element !== "div") {
      console.warn(
        "Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"
      );
    }

    if (this.options !== undefined) {
      console.warn(
        "Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props"
      );
    }
  },

  mounted() {
    this.noneFunctionalComponentMode =
      this.getTag().toLowerCase() !== this.$el.nodeName.toLowerCase();
    if (this.noneFunctionalComponentMode && this.transitionMode) {
      throw new Error(
        `Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ${this.getTag()}`
      );
    }
    var optionsAdded = {};
    eventsListened.forEach(elt => {
      optionsAdded["on" + elt] = delegateAndEmit.call(this, elt);
    });

    eventsToEmit.forEach(elt => {
      optionsAdded["on" + elt] = emit.bind(this, elt);
    });

    const attributes = Object.keys(this.$attrs).reduce((res, key) => {
      res[camelize(key)] = this.$attrs[key];
      return res;
    }, {});

    const options = Object.assign({}, this.options, attributes, optionsAdded, {
      onMove: (evt, originalEvent) => {
        return this.onDragMove(evt, originalEvent);
      }
    });
    !("draggable" in options) && (options.draggable = ">*");
    this._sortable = new Sortable(this.rootContainer, options);
    this.computeIndexes();
  },

  beforeDestroy() {
    if (this._sortable !== undefined) this._sortable.destroy();
  },

  computed: {
    rootContainer() {
      return this.transitionMode ? this.$el.children[0] : this.$el;
    },

    realList() {
      return this.list ? this.list : this.value;
    }
  },

  watch: {
    options: {
      handler(newOptionValue) {
        this.updateOptions(newOptionValue);
      },
      deep: true
    },

    $attrs: {
      handler(newOptionValue) {
        this.updateOptions(newOptionValue);
      },
      deep: true
    },

    realList() {
      this.computeIndexes();
    }
  },

  methods: {
    getTag() {
      return this.tag || this.element;
    },

    updateOptions(newOptionValue) {
      for (var property in newOptionValue) {
        const value = camelize(property);
        if (readonlyProperties.indexOf(value) == -1) {
          this._sortable.option(value, newOptionValue[property]);
        }
      }
    },

    getChildrenNodes() {
      if (!this.init) {
        this.noneFunctionalComponentMode =
          this.noneFunctionalComponentMode && this.$children.length == 1;
        this.init = true;
      }

      if (this.noneFunctionalComponentMode) {
        return this.$children[0].$slots.default;
      }
      const rawNodes = this.$slots.default;
      return this.transitionMode ? rawNodes[0].child.$slots.default : rawNodes;
    },

    computeIndexes() {
      this.$nextTick(() => {
        this.visibleIndexes = computeIndexes(
          this.getChildrenNodes(),
          this.rootContainer.children,
          this.transitionMode,
          this.footerOffset
        );
      });
    },

    getUnderlyingVm(htmlElt) {
      const index = computeVmIndex(this.getChildrenNodes() || [], htmlElt);
      if (index === -1) {
        //Edge case during move callback: related element might be
        //an element different from collection
        return null;
      }
      const element = this.realList[index];
      return { index, element };
    },

    getUnderlyingPotencialDraggableComponent({ __vue__ }) {
      if (
        !__vue__ ||
        !__vue__.$options ||
        __vue__.$options._componentTag !== "transition-group"
      ) {
        return __vue__;
      }
      return __vue__.$parent;
    },

    emitChanges(evt) {
      this.$nextTick(() => {
        this.$emit("change", evt);
      });
    },

    alterList(onList) {
      if (this.list) {
        onList(this.list);
        return;
      }
      const newList = [...this.value];
      onList(newList);
      this.$emit("input", newList);
    },

    spliceList() {
      const spliceList = list => list.splice(...arguments);
      this.alterList(spliceList);
    },

    updatePosition(oldIndex, newIndex) {
      const updatePosition = list =>
        list.splice(newIndex, 0, list.splice(oldIndex, 1)[0]);
      this.alterList(updatePosition);
    },

    getRelatedContextFromMoveEvent({ to, related }) {
      const component = this.getUnderlyingPotencialDraggableComponent(to);
      if (!component) {
        return { component };
      }
      const list = component.realList;
      const context = { list, component };
      if (to !== related && list && component.getUnderlyingVm) {
        const destination = component.getUnderlyingVm(related);
        if (destination) {
          return Object.assign(destination, context);
        }
      }
      return context;
    },

    getVmIndex(domIndex) {
      const indexes = this.visibleIndexes;
      const numberIndexes = indexes.length;
      return domIndex > numberIndexes - 1 ? numberIndexes : indexes[domIndex];
    },

    getComponent() {
      return this.$slots.default[0].componentInstance;
    },

    resetTransitionData(index) {
      if (!this.noTransitionOnDrag || !this.transitionMode) {
        return;
      }
      var nodes = this.getChildrenNodes();
      nodes[index].data = null;
      const transitionContainer = this.getComponent();
      transitionContainer.children = [];
      transitionContainer.kept = undefined;
    },

    onDragStart(evt) {
      this.context = this.getUnderlyingVm(evt.item);
      evt.item._underlying_vm_ = this.clone(this.context.element);
      draggingElement = evt.item;
    },

    onDragAdd(evt) {
      const element = evt.item._underlying_vm_;
      if (element === undefined) {
        return;
      }
      removeNode(evt.item);
      const newIndex = this.getVmIndex(evt.newIndex);
      this.spliceList(newIndex, 0, element);
      this.computeIndexes();
      const added = { element, newIndex };
      this.emitChanges({ added });
    },

    onDragRemove(evt) {
      insertNodeAt(this.rootContainer, evt.item, evt.oldIndex);
      if (evt.pullMode === "clone") {
        removeNode(evt.clone);
        return;
      }
      const oldIndex = this.context.index;
      this.spliceList(oldIndex, 1);
      const removed = { element: this.context.element, oldIndex };
      this.resetTransitionData(oldIndex);
      this.emitChanges({ removed });
    },

    onDragUpdate(evt) {
      removeNode(evt.item);
      insertNodeAt(evt.from, evt.item, evt.oldIndex);
      const oldIndex = this.context.index;
      const newIndex = this.getVmIndex(evt.newIndex);
      this.updatePosition(oldIndex, newIndex);
      const moved = { element: this.context.element, oldIndex, newIndex };
      this.emitChanges({ moved });
    },

    updateProperty(evt, propertyName) {
      evt.hasOwnProperty(propertyName) &&
        (evt[propertyName] += this.headerOffset);
    },

    computeFutureIndex(relatedContext, evt) {
      if (!relatedContext.element) {
        return 0;
      }
      const domChildren = [...evt.to.children].filter(
        el => el.style["display"] !== "none"
      );
      const currentDOMIndex = domChildren.indexOf(evt.related);
      const currentIndex = relatedContext.component.getVmIndex(currentDOMIndex);
      const draggedInList = domChildren.indexOf(draggingElement) != -1;
      return draggedInList || !evt.willInsertAfter
        ? currentIndex
        : currentIndex + 1;
    },

    onDragMove(evt, originalEvent) {
      const onMove = this.move;
      if (!onMove || !this.realList) {
        return true;
      }

      const relatedContext = this.getRelatedContextFromMoveEvent(evt);
      const draggedContext = this.context;
      const futureIndex = this.computeFutureIndex(relatedContext, evt);
      Object.assign(draggedContext, { futureIndex });
      const sendEvt = Object.assign({}, evt, {
        relatedContext,
        draggedContext
      });
      return onMove(sendEvt, originalEvent);
    },

    onDragEnd() {
      this.computeIndexes();
      draggingElement = null;
    }
  }
};

if (typeof window !== "undefined" && "Vue" in window) {
  window.Vue.component("draggable", draggableComponent);
}

export default draggableComponent;
<|endoftext|>