<commit_before>/**
 * social-share.js
 *
 * @author  52cik <fe.52cik@gmail.com>
 * @license MIT
 *
 * @example
 *

 * socialShare('.share-components');
 *
 * // or
 *
 * socialShare('.share-bar', {
 *     sites: ['qzone', 'qq', 'weibo','wechat'],
 *     // ...
 * });
 *

 */
;(function (window, document, undefined) {

    // Initialize a variables.

    var Array$indexOf = Array.prototype.indexOf;
    var Object$assign = Object.assign;

    var runningInWeChat = /MicroMessenger/i.test(navigator.userAgent);
    var isMobileScreen = document.documentElement.clientWidth <= 768;

    var image = (document.images[0] || 0).src || '';
    var site = getMetaContentByName('site') || getMetaContentByName('Site') || document.title;
    var title = getMetaContentByName('title') || getMetaContentByName('Title') || document.title;
    var description = getMetaContentByName('description') || getMetaContentByName('Description') || '';

    var defaults = {
        url: location.href,
        origin: location.origin,
        source: site,
        title: title,
        description: description,
        image: image,
        imageSelector: undefined,

        weiboKey: '',

        wechatQrcodeTitle: '微信扫一扫：分享',
        wechatQrcodeHelper: '
微信里点“发现”，扫一下

二维码便可将本文分享至朋友圈。
',
        wechatQrcodeSize: 100,

        sites: ['weibo', 'qq', 'wechat', 'tencent', 'douban', 'qzone', 'linkedin', 'diandian', 'facebook', 'twitter', 'google'],
        mobileSites: [],
        disabled: [],
        initialized: false
    };

    var templates = {
        qzone: 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url={{URL}}&title={{TITLE}}&desc={{DESCRIPTION}}&summary={{SUMMARY}}&site={{SOURCE}}',
        qq: 'http://connect.qq.com/widget/shareqq/index.html?url={{URL}}&title={{TITLE}}&source={{SOURCE}}&desc={{DESCRIPTION}}&pics={{IMAGE}}&summary="{{SUMMARY}}"',
        tencent: 'http://share.v.t.qq.com/index.php?c=share&a=index&title={{TITLE}}&url={{URL}}&pic={{IMAGE}}',
        weibo: 'http://service.weibo.com/share/share.php?url={{URL}}&title={{TITLE}}&pic={{IMAGE}}&appkey={{WEIBOKEY}}',
        wechat: 'javascript:',
        douban: 'http://shuo.douban.com/!service/share?href={{URL}}&name={{TITLE}}&text={{DESCRIPTION}}&image={{IMAGE}}&starid=0&aid=0&style=11',
        diandian: 'http://www.diandian.com/share?lo={{URL}}&ti={{TITLE}}&type=link',
        linkedin: 'http://www.linkedin.com/shareArticle?mini=true&ro=true&title={{TITLE}}&url={{URL}}&summary={{SUMMARY}}&source={{SOURCE}}&armin=armin',
        facebook: 'https://www.facebook.com/sharer/sharer.php?u={{URL}}',
        twitter: 'https://twitter.com/intent/tweet?text={{TITLE}}&url={{URL}}&via={{ORIGIN}}',
        google: 'https://plus.google.com/share?url={{URL}}'
    };

    /**
     * Expose API to the global
     *
     * @param  {String|Element} elem
     * @param  {Object} options
     */
    window.socialShare = function (elem, options) {
        elem = typeof elem === 'string' ? querySelectorAlls(elem) : elem;

        if (elem.length === undefined) {
            elem = [elem];
        }

        each(elem, function (el) {
            if (!el.initialized) {
                share(el, options);
            }
        });
    };

    // Domready after initialization
    alReady(function () {
        socialShare('.social-share, .share-component');
    });

    /**
     * Initialize a share bar.
     *
     * @param {Object}        $options globals (optional).
     *
     * @return {Void}
     */
    function share(elem, options) {
        var data = mixin({}, defaults, options || {}, dataset(elem));

        if (data.imageSelector) {
            data.image = querySelectorAlls(data.imageSelector).map(function(item) {
                return item.src;
            }).join('||');
        }

        addClass(elem, 'share-component social-share');
        createIcons(elem, data);
        createWechat(elem, data);

        elem.initialized = true;
    }

    /**
     * Create site icons
     *
     * @param {Element} elem
     * @param {Object} data
     */
    function createIcons(elem, data) {
        var sites = getSites(data);
        var isPrepend = data.mode == 'prepend';

        each(isPrepend ? sites.reverse() : sites, function (name) {
            var url = makeUrl(name, data);
            var link = data.initialized ? getElementsByClassName(elem, 'icon-' + name) : createElementByString('');

            if (!link.length) {
                return true;
            }

            link[0].href = url;

            if (name === 'wechat') {
                link[0].tabindex = -1;
            } else {
                link[0].target = '_blank';
            }

            if (!data.initialized) {
                isPrepend ? elem.insertBefore(link[0], elem.firstChild) : elem.appendChild(link[0]);
            }
        });
    }

    /**
     * Create the wechat icon and QRCode.
     *
     * @param {Element} elem
     * @param {Object} data
     */
    function createWechat (elem, data) {
        var wechat = getElementsByClassName(elem, 'icon-wechat', 'a');

        if (wechat.length === 0) {
            return false;
        }

        var elems = createElementByString('

' + data.wechatQrcodeTitle + '

' + data.wechatQrcodeHelper + '

');
        var qrcode = getElementsByClassName(elems[0], 'qrcode', 'div');

        wechat[0].appendChild(elems[0]);
        new QRCode(qrcode[0], {text: data.url, width: data.wechatQrcodeSize, height: data.wechatQrcodeSize});
    }

    /**
     * Get available site lists.
     *
     * @param {Object} data
     *
     * @returns {Array}
     */
    function getSites(data) {
        if (!data['mobileSites'].length) {
            data['mobileSites'] = data['sites'];
        }

        var sites = (isMobileScreen ? data['mobileSites'] : data['sites']).slice(0);
        var disabled = data['disabled'];

        if (typeof sites == 'string') {
            sites = sites.split(/\s*,\s*/);
        }
        if (typeof disabled == 'string') {
            disabled = disabled.split(/\s*,\s*/);
        }

        if (runningInWeChat) {
            disabled.push('wechat');
        }

        // Remove elements
        disabled.length && each(disabled, function (it) {
            sites.splice(inArray(it, sites), 1);
        });

        return sites;
    }

    /**
     * Build the url of icon.
     *
     * @param {String} name
     * @param {Object} data
     *
     * @returns {String}
     */
    function makeUrl(name, data) {

        if (! data['summary']){
            data['summary'] = data['description'];
        }

        return templates[name].replace(/\{\{(\w)(\w*)\}\}/g, function (m, fix, key) {
            var nameKey = name + fix + key.toLowerCase();
            key = (fix + key).toLowerCase();

            return encodeURIComponent((data[nameKey] === undefined ? data[key] : data[nameKey]) || '');
        });
    }

    /**
     * Supports querySelectorAll, jQuery, Zepto and simple selector.
     *
     * @param str
     *
     * @returns {*}
     */
    function querySelectorAlls(str) {
        return (document.querySelectorAll || window.jQuery || window.Zepto || selector).call(document, str);
    }

    /**
     * Simple selector.
     *
     * @param {String} str #ID or .CLASS
     *
     * @returns {Array}
     */
    function selector(str) {
        var elems = [];

        each(str.split(/\s*,\s*/), function(s) {
            var m = s.match(/([#.])(\w+)/);
            if (m === null) {
                throw Error('Supports only simple single #ID or .CLASS selector.');
            }

            if (m[1]) {
                var elem = document.getElementById(m[2]);

                if (elem) {
                    elems.push(elem);
                }
            }

            elems = elems.concat(getElementsByClassName(str));
        });

        return elems;
    }

    /**
     * Add the classNames for element.
     *
     * @param {Element} elem
     * @param {String} value
     */
    function addClass(elem, value) {
        if (value && typeof value === "string") {
            var classNames = (elem.className + ' ' + value).split(/\s+/);
            var setClass = ' ';

            each(classNames, function (className) {
                if (setClass.indexOf(' ' + className + ' ') < 0) {
                    setClass += className + ' ';
                }
            });

            elem.className = setClass.slice(1, -1);
        }
    }

    /**
     * Get meta element content value
     *
     * @param {String} name
     *
     * @returns {String|*}
     */
    function getMetaContentByName(name) {
        return (document.getElementsByName(name)[0] || 0).content;
    }

    /**
     * Get elements By className for IE8-
     *
     * @param {Element} elem element
     * @param {String} name className
     * @param {String} tag tagName
     *
     * @returns {HTMLCollection|Array}
     */
    function getElementsByClassName(elem, name, tag) {
        if (elem.getElementsByClassName) {
            return elem.getElementsByClassName(name);
        }

        var elements = [];
        var elems = elem.getElementsByTagName(tag || '*');
        name = ' ' + name + ' ';

        each(elems, function (elem) {
            if ((' ' + (elem.className || '') + ' ').indexOf(name) >= 0) {
                elements.push(elem);
            }
        });

        return elements;
    }

    /**
     * Create element by string.
     *
     * @param {String} str
     *
     * @returns {NodeList}
     */
    function createElementByString(str) {
        var div = document.createElement('div');
        div.innerHTML = str;

        return div.childNodes;
    }

    /**
     * Merge objects.
     *
     * @returns {Object}
     */
    function mixin() {
        var args = arguments;

        if (Object$assign) {
            return Object$assign.apply(null, args);
        }

        var target = {};

        each(args, function (it) {
            each(it, function (v, k) {
                target[k] = v;
            });
        });

        return args[0] = target;
    }

    /**
     * Get dataset object.
     *
     * @param {Element} elem
     *
     * @returns {Object}
     */
    function dataset(elem) {
        if (elem.dataset) {
            return JSON.parse(JSON.stringify(elem.dataset));
        }

        var target = {};

        if (elem.hasAttributes()) {
            each(elem.attributes, function (attr) {
                var name = attr.name;
                if (name.indexOf('data-') !== 0) {
                    return true;
                }

                name = name.replace(/^data-/i, '')
                    .replace(/-(\w)/g, function (all, letter) {
                        return letter.toUpperCase();
                    });

                target[name] = attr.value;
            });

            return target;
        }

        return {};
    }

    /**
     * found element in the array.
     *
     * @param {Array|Object} elem
     * @param {Array} arr
     * @param {Number} i
     *
     * @returns {Number}
     */
    function inArray(elem, arr, i) {
        var len;

        if (arr) {
            if (Array$indexOf) {
                return Array$indexOf.call(arr, elem, i);
            }

            len = arr.length;
            i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

            for (; i < len; i++) {
                // Skip accessing in sparse arrays
                if (i in arr && arr[i] === elem) {
                    return i;
                }
            }
        }

        return -1;
    }

    /**
     * Simple each.
     *
     * @param {Array|Object} obj
     * @param {Function} callback
     *
     * @returns {*}
     */
    function each(obj, callback) {
        var length = obj.length;

        if (length === undefined) {
            for (var name in obj) {
                if (obj.hasOwnProperty(name)) {
                    if (callback.call(obj[name], obj[name], name) === false) {
                        break;
                    }
                }
            }
        } else {
            for (var i = 0; i < length; i++) {
                if (callback.call(obj[i], obj[i], i) === false) {
                    break;
                }
            }
        }
    }

    /**
     * Dom ready.
     *
     * @param {Function} fn
     *
     * @link https://github.com/jed/alReady.js
     */
    function alReady ( fn ) {
        var add = 'addEventListener';
        var pre = document[ add ] ? '' : 'on';

        ~document.readyState.indexOf( 'm' ) ? fn() :
            'load DOMContentLoaded readystatechange'.replace( /\w+/g, function( type, i ) {
                ( i ? document : window )
                    [ pre ? 'attachEvent' : add ]
                (
                    pre + type,
                    function(){ if ( fn ) if ( i < 6 || ~document.readyState.indexOf( 'm' ) ) fn(), fn = 0 },
                    !1
                )
            })
    }
})(window, document);
<commit_msg>删除无用的空行<commit_after>/**
 * social-share.js
 *
 * @author  52cik <fe.52cik@gmail.com>
 * @license MIT
 *
 * @example
 *

 * socialShare('.share-components');
 *
 * // or
 *
 * socialShare('.share-bar', {
 *     sites: ['qzone', 'qq', 'weibo','wechat'],
 *     // ...
 * });
 *

 */
;(function (window, document, undefined) {

    // Initialize a variables.

    var Array$indexOf = Array.prototype.indexOf;
    var Object$assign = Object.assign;

    var runningInWeChat = /MicroMessenger/i.test(navigator.userAgent);
    var isMobileScreen = document.documentElement.clientWidth <= 768;

    var image = (document.images[0] || 0).src || '';
    var site = getMetaContentByName('site') || getMetaContentByName('Site') || document.title;
    var title = getMetaContentByName('title') || getMetaContentByName('Title') || document.title;
    var description = getMetaContentByName('description') || getMetaContentByName('Description') || '';

    var defaults = {
        url: location.href,
        origin: location.origin,
        source: site,
        title: title,
        description: description,
        image: image,
        imageSelector: undefined,

        weiboKey: '',

        wechatQrcodeTitle: '微信扫一扫：分享',
        wechatQrcodeHelper: '
微信里点“发现”，扫一下

二维码便可将本文分享至朋友圈。
',
        wechatQrcodeSize: 100,

        sites: ['weibo', 'qq', 'wechat', 'tencent', 'douban', 'qzone', 'linkedin', 'diandian', 'facebook', 'twitter', 'google'],
        mobileSites: [],
        disabled: [],
        initialized: false
    };

    var templates = {
        qzone: 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url={{URL}}&title={{TITLE}}&desc={{DESCRIPTION}}&summary={{SUMMARY}}&site={{SOURCE}}',
        qq: 'http://connect.qq.com/widget/shareqq/index.html?url={{URL}}&title={{TITLE}}&source={{SOURCE}}&desc={{DESCRIPTION}}&pics={{IMAGE}}&summary="{{SUMMARY}}"',
        tencent: 'http://share.v.t.qq.com/index.php?c=share&a=index&title={{TITLE}}&url={{URL}}&pic={{IMAGE}}',
        weibo: 'http://service.weibo.com/share/share.php?url={{URL}}&title={{TITLE}}&pic={{IMAGE}}&appkey={{WEIBOKEY}}',
        wechat: 'javascript:',
        douban: 'http://shuo.douban.com/!service/share?href={{URL}}&name={{TITLE}}&text={{DESCRIPTION}}&image={{IMAGE}}&starid=0&aid=0&style=11',
        diandian: 'http://www.diandian.com/share?lo={{URL}}&ti={{TITLE}}&type=link',
        linkedin: 'http://www.linkedin.com/shareArticle?mini=true&ro=true&title={{TITLE}}&url={{URL}}&summary={{SUMMARY}}&source={{SOURCE}}&armin=armin',
        facebook: 'https://www.facebook.com/sharer/sharer.php?u={{URL}}',
        twitter: 'https://twitter.com/intent/tweet?text={{TITLE}}&url={{URL}}&via={{ORIGIN}}',
        google: 'https://plus.google.com/share?url={{URL}}'
    };

    /**
     * Expose API to the global
     *
     * @param  {String|Element} elem
     * @param  {Object} options
     */
    window.socialShare = function (elem, options) {
        elem = typeof elem === 'string' ? querySelectorAlls(elem) : elem;

        if (elem.length === undefined) {
            elem = [elem];
        }

        each(elem, function (el) {
            if (!el.initialized) {
                share(el, options);
            }
        });
    };

    // Domready after initialization
    alReady(function () {
        socialShare('.social-share, .share-component');
    });

    /**
     * Initialize a share bar.
     *
     * @param {Object}        $options globals (optional).
     *
     * @return {Void}
     */
    function share(elem, options) {
        var data = mixin({}, defaults, options || {}, dataset(elem));

        if (data.imageSelector) {
            data.image = querySelectorAlls(data.imageSelector).map(function(item) {
                return item.src;
            }).join('||');
        }

        addClass(elem, 'share-component social-share');
        createIcons(elem, data);
        createWechat(elem, data);

        elem.initialized = true;
    }

    /**
     * Create site icons
     *
     * @param {Element} elem
     * @param {Object} data
     */
    function createIcons(elem, data) {
        var sites = getSites(data);
        var isPrepend = data.mode == 'prepend';

        each(isPrepend ? sites.reverse() : sites, function (name) {
            var url = makeUrl(name, data);
            var link = data.initialized ? getElementsByClassName(elem, 'icon-' + name) : createElementByString('');

            if (!link.length) {
                return true;
            }

            link[0].href = url;

            if (name === 'wechat') {
                link[0].tabindex = -1;
            } else {
                link[0].target = '_blank';
            }

            if (!data.initialized) {
                isPrepend ? elem.insertBefore(link[0], elem.firstChild) : elem.appendChild(link[0]);
            }
        });
    }

    /**
     * Create the wechat icon and QRCode.
     *
     * @param {Element} elem
     * @param {Object} data
     */
    function createWechat (elem, data) {
        var wechat = getElementsByClassName(elem, 'icon-wechat', 'a');

        if (wechat.length === 0) {
            return false;
        }

        var elems = createElementByString('

' + data.wechatQrcodeTitle + '

' + data.wechatQrcodeHelper + '

');
        var qrcode = getElementsByClassName(elems[0], 'qrcode', 'div');

        wechat[0].appendChild(elems[0]);
        new QRCode(qrcode[0], {text: data.url, width: data.wechatQrcodeSize, height: data.wechatQrcodeSize});
    }

    /**
     * Get available site lists.
     *
     * @param {Object} data
     *
     * @returns {Array}
     */
    function getSites(data) {
        if (!data['mobileSites'].length) {
            data['mobileSites'] = data['sites'];
        }

        var sites = (isMobileScreen ? data['mobileSites'] : data['sites']).slice(0);
        var disabled = data['disabled'];

        if (typeof sites == 'string') {
            sites = sites.split(/\s*,\s*/);
        }
        if (typeof disabled == 'string') {
            disabled = disabled.split(/\s*,\s*/);
        }

        if (runningInWeChat) {
            disabled.push('wechat');
        }

        // Remove elements
        disabled.length && each(disabled, function (it) {
            sites.splice(inArray(it, sites), 1);
        });

        return sites;
    }

    /**
     * Build the url of icon.
     *
     * @param {String} name
     * @param {Object} data
     *
     * @returns {String}
     */
    function makeUrl(name, data) {

        if (! data['summary']){
            data['summary'] = data['description'];
        }

        return templates[name].replace(/\{\{(\w)(\w*)\}\}/g, function (m, fix, key) {
            var nameKey = name + fix + key.toLowerCase();
            key = (fix + key).toLowerCase();

            return encodeURIComponent((data[nameKey] === undefined ? data[key] : data[nameKey]) || '');
        });
    }

    /**
     * Supports querySelectorAll, jQuery, Zepto and simple selector.
     *
     * @param str
     *
     * @returns {*}
     */
    function querySelectorAlls(str) {
        return (document.querySelectorAll || window.jQuery || window.Zepto || selector).call(document, str);
    }

    /**
     * Simple selector.
     *
     * @param {String} str #ID or .CLASS
     *
     * @returns {Array}
     */
    function selector(str) {
        var elems = [];

        each(str.split(/\s*,\s*/), function(s) {
            var m = s.match(/([#.])(\w+)/);
            if (m === null) {
                throw Error('Supports only simple single #ID or .CLASS selector.');
            }

            if (m[1]) {
                var elem = document.getElementById(m[2]);

                if (elem) {
                    elems.push(elem);
                }
            }

            elems = elems.concat(getElementsByClassName(str));
        });

        return elems;
    }

    /**
     * Add the classNames for element.
     *
     * @param {Element} elem
     * @param {String} value
     */
    function addClass(elem, value) {
        if (value && typeof value === "string") {
            var classNames = (elem.className + ' ' + value).split(/\s+/);
            var setClass = ' ';

            each(classNames, function (className) {
                if (setClass.indexOf(' ' + className + ' ') < 0) {
                    setClass += className + ' ';
                }
            });

            elem.className = setClass.slice(1, -1);
        }
    }

    /**
     * Get meta element content value
     *
     * @param {String} name
     *
     * @returns {String|*}
     */
    function getMetaContentByName(name) {
        return (document.getElementsByName(name)[0] || 0).content;
    }

    /**
     * Get elements By className for IE8-
     *
     * @param {Element} elem element
     * @param {String} name className
     * @param {String} tag tagName
     *
     * @returns {HTMLCollection|Array}
     */
    function getElementsByClassName(elem, name, tag) {
        if (elem.getElementsByClassName) {
            return elem.getElementsByClassName(name);
        }

        var elements = [];
        var elems = elem.getElementsByTagName(tag || '*');
        name = ' ' + name + ' ';

        each(elems, function (elem) {
            if ((' ' + (elem.className || '') + ' ').indexOf(name) >= 0) {
                elements.push(elem);
            }
        });

        return elements;
    }

    /**
     * Create element by string.
     *
     * @param {String} str
     *
     * @returns {NodeList}
     */
    function createElementByString(str) {
        var div = document.createElement('div');
        div.innerHTML = str;

        return div.childNodes;
    }

    /**
     * Merge objects.
     *
     * @returns {Object}
     */
    function mixin() {
        var args = arguments;

        if (Object$assign) {
            return Object$assign.apply(null, args);
        }

        var target = {};

        each(args, function (it) {
            each(it, function (v, k) {
                target[k] = v;
            });
        });

        return args[0] = target;
    }

    /**
     * Get dataset object.
     *
     * @param {Element} elem
     *
     * @returns {Object}
     */
    function dataset(elem) {
        if (elem.dataset) {
            return JSON.parse(JSON.stringify(elem.dataset));
        }

        var target = {};

        if (elem.hasAttributes()) {
            each(elem.attributes, function (attr) {
                var name = attr.name;
                if (name.indexOf('data-') !== 0) {
                    return true;
                }

                name = name.replace(/^data-/i, '')
                    .replace(/-(\w)/g, function (all, letter) {
                        return letter.toUpperCase();
                    });

                target[name] = attr.value;
            });

            return target;
        }

        return {};
    }

    /**
     * found element in the array.
     *
     * @param {Array|Object} elem
     * @param {Array} arr
     * @param {Number} i
     *
     * @returns {Number}
     */
    function inArray(elem, arr, i) {
        var len;

        if (arr) {
            if (Array$indexOf) {
                return Array$indexOf.call(arr, elem, i);
            }

            len = arr.length;
            i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

            for (; i < len; i++) {
                // Skip accessing in sparse arrays
                if (i in arr && arr[i] === elem) {
                    return i;
                }
            }
        }

        return -1;
    }

    /**
     * Simple each.
     *
     * @param {Array|Object} obj
     * @param {Function} callback
     *
     * @returns {*}
     */
    function each(obj, callback) {
        var length = obj.length;

        if (length === undefined) {
            for (var name in obj) {
                if (obj.hasOwnProperty(name)) {
                    if (callback.call(obj[name], obj[name], name) === false) {
                        break;
                    }
                }
            }
        } else {
            for (var i = 0; i < length; i++) {
                if (callback.call(obj[i], obj[i], i) === false) {
                    break;
                }
            }
        }
    }

    /**
     * Dom ready.
     *
     * @param {Function} fn
     *
     * @link https://github.com/jed/alReady.js
     */
    function alReady ( fn ) {
        var add = 'addEventListener';
        var pre = document[ add ] ? '' : 'on';

        ~document.readyState.indexOf( 'm' ) ? fn() :
            'load DOMContentLoaded readystatechange'.replace( /\w+/g, function( type, i ) {
                ( i ? document : window )
                    [ pre ? 'attachEvent' : add ]
                (
                    pre + type,
                    function(){ if ( fn ) if ( i < 6 || ~document.readyState.indexOf( 'm' ) ) fn(), fn = 0 },
                    !1
                )
            })
    }
})(window, document);
<|endoftext|>


<commit_before>Blend.defineClass('Blend.ui.Container', {
    extend: 'Blend.ui.Component',
    alias: 'ui.container',
    items: null,
    init: function () {
        var me = this;
        me.callParent.apply(me, arguments);
        me.items = Blend.wrapInArray(me.items || []);
    },
    initElement: function (el) {
        var me = this;
        el = me.callParent.apply(me, arguments);
        el.items = el.items = [];
        me.createBodyElement(el);
        return el;
    },
    renderItems: function () {
        var me = this, items = [], view;
        Blend.foreach(me.items, function (item, idx) {
            view = me.createChildView(item);
            me.items[idx] = view;
            items.push(view.getElement({
                cls: [Blend.cssPrefix('conatiner-item')]
            }));
        });
        return items;
    },
    createChildView: function (viewCfg) {
        var me = this;
        return Blend.ui.Component.createView.apply(me, arguments);
    },
    createBodyElement: function (el) {
        var me = this;
        el.items.push({
            oid: 'bodyEl',
            cls: [Blend.cssPrefix('container-body')],
            items: me.renderItems()
        });
    },
    finalizeRender: function (el) {
        var me = this;
        return el;
    }
});<commit_msg>Implemented initial container layout lifecycle<commit_after>Blend.defineClass('Blend.ui.Container', {
    extend: 'Blend.ui.Component',
    alias: 'ui.container',
    layout: 'default',
    requires: [
        'Blend.layout.container.Default'
    ],
    items: null,
    init: function () {
        var me = this;
        me.callParent.apply(me, arguments);
        me.items = Blend.wrapInArray(me.items || []);
        if (!Blend.isInstanceOf(me.layout, Blend.layout.container.Layout)) {
            throw new Error('Invalid layout type provided for this container. A container layout type was expected!');
        }
    },
    initElement: function (el) {
        var me = this;
        el = me.callParent.apply(me, arguments);
        el.items = el.items = [];
        me.createBodyElement(el);
        return el;
    },
    createChildViews: function () {
        var me = this, view;
        Blend.foreach(me.items, function (item, idx) {
            view = me.createChildView(item);
            me.items[idx] = view;
        });
        me.items = me.layout.processChildViews(me.items);
    },
    renderItems: function () {
        var me = this, items = [];
        me.createChildViews();
        Blend.foreach(me.items, function (view, idx) {
            items.push(view.getElement({
                cls: [Blend.cssPrefix('conatiner-item')]
            }));
        });
        return me.layout.processChildElements(items);
    },
    createChildView: function (viewCfg) {
        var me = this;
        return Blend.ui.Component.createView.apply(me, arguments);
    },
    createBodyElement: function (el) {
        var me = this;
        el.items.push({
            oid: 'bodyEl',
            cls: [Blend.cssPrefix('container-body')],
            items: me.renderItems()
        });
    },
    finalizeRender: function (el) {
        var me = this;
        me.layout.processViewElement(me.bodyEl);
        return el;
    }
});<|endoftext|>


<commit_before>module.exports = (bot, logger, helper) => {
  const Whatanime = require('whatanimega-helper')

  const Format = class {
    constructor (time) {
      this.time = time
      return true
    }
    get hour () {
      return this.pad(Math.floor(this.time / (60 * 60)))
    }
    get min () {
      return this.pad(Math.floor(this.time % (60 * 60) / 60))
    }
    get sec () {
      return this.pad(Math.floor(this.time % 60))
    }
    pad (s) {
      return (s < 10 ? '0' : '') + s
    }
  }

  const failure = async (chatid, msg) => {
    let temp
    try {
      // eslint-disable-next-line
      let send
      [send, temp] = await Promise.all([
        bot.sendChatAction(chatid, 'typing'),
        helper.getlang(msg, logger)
      ])
      await bot.sendMessage(chatid, '📺❗ ' + temp.text(msg.chat.type, 'command.whatanime.info'), {
        reply_to_message_id: msg.message_id,
        parse_mode: 'HTML',
        reply_markup: {
          force_reply: true, selective: true
        }
      })
      logger.info('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: valid,')
    } catch (e) {
      logger.error('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: error')
      logger.debug(e.stack)
    }
  }

  const success = async (chatid, msg, photo) => {
    let temp
    try {
      logger.info('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: command received')
      // eslint-disable-next-line
      let send
      [send, temp] = await Promise.all([
        bot.sendChatAction(chatid, 'typing'),
        helper.getlang(msg, logger)
      ])

      const query = new Whatanime(global.config.apikey.whatanime)

      const url = await bot.getFileLink(photo)
      const response = await query.search(url)
      const result = response.docs[0]
      let resultMessage = ''
      if (result.anime.toLowerCase() !== result.title_english.toLowerCase()) {
        resultMessage = temp.textb(msg.chat.type, 'command.whatanime.name') + ': ' + result.title + '\n' +
          temp.textb(msg.chat.type, 'command.whatanime.english') + ': ' + result.title_english + '\n'
      } else {
        resultMessage = temp.textb(msg.chat.type, 'command.whatanime.name') + ': ' + result.anime + '\n'
      }
      const time = new Format(result.at)
      resultMessage = resultMessage +
        temp.textb(msg.chat.type, 'command.whatanime.episode') + ' ' + result.episode + '\n' +
        temp.textb(msg.chat.type, 'command.whatanime.time') + ': ' +
        (time.hour === '00' ? '' : time.hour + ' : ') + time.min + ' : ' + time.sec + '\n' +
        temp.textb(msg.chat.type, 'command.whatanime.match') + ': ' + (result.similarity * 100).toFixed(2) + '%'
      if (result.similarity * 100 < 70) {
        await bot.sendMessage(chatid, resultMessage + '\n' + temp.text(msg.chat.type, 'command.whatanime.incorrect'), {
          parse_mode: 'HTML',
          disable_web_page_preview: true,
          reply_to_message_id: msg.message_id
        })
      } else {
        const animeVideo = await query.previewVideo(result.season, result.anime, result.filename, result.at, result.tokenthumb)
        await Promise.all([
          bot.sendMessage(chatid, resultMessage, {
            parse_mode: 'HTML',
            disable_web_page_preview: true,
            reply_to_message_id: msg.message_id
          }),
          bot.sendVideo(chatid, animeVideo, {
            reply_to_message_id: msg.message_id
          })
        ])
      }
      logger.info('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: valid')
    } catch (e) {
      logger.error('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: error')
      logger.debug(e.stack)
    }
  }

  bot.on('message', async (msg) => {
    // eslint-disable-next-line
    const regex1 = new RegExp('^(?:무슨애니\??|whatanime|anime|\/(?:무슨애니|whatanime)+(?:@' + global.botinfo.username + ')? ?)$')
    const regex2 = new RegExp('/(?:무슨애니|whatanime)+(?:@' + global.botinfo.username + ')? ?$')
    try {
      if (Math.round((new Date()).getTime() / 1000) - msg.date >= 180) return
      if (msg.photo) {
        if (regex1.test(msg.caption)) {
          await success(msg.chat.id, msg, msg.photo[msg.photo.length - 1].file_id)
          return
        } else if (msg.reply_to_message && msg.reply_to_message.from &&
          msg.reply_to_message.from.username === global.botinfo.username &&
          msg.reply_to_message.text.match(/📺❗/)) {
          await success(msg.chat.id, msg, msg.photo[msg.photo.length - 1].file_id)
          return
        }
      } else {
        if (regex1.test(msg.text) &&
          msg.reply_to_message && msg.reply_to_message.photo) {
          await success(msg.chat.id, msg, msg.reply_to_message.photo[msg.reply_to_message.photo.length - 1].file_id)
          return
        } else if (regex2.test(msg.text)) {
          await failure(msg.chat.id, msg)
          return
        }
      }
    } catch (e) {
      logger.error('chatid: ' + msg.chat.id + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: error')
      logger.debug(e.stack)
    }
  })
}
<commit_msg>m_amine.js 수정완료<commit_after>module.exports = (bot, logger, helper) => {
  const Whatanime = require('whatanimega-helper')

  const Format = class {
    constructor (time) {
      this.time = time
      return true
    }
    get hour () {
      return this.pad(Math.floor(this.time / (60 * 60)))
    }
    get min () {
      return this.pad(Math.floor(this.time % (60 * 60) / 60))
    }
    get sec () {
      return this.pad(Math.floor(this.time % 60))
    }
    pad (s) {
      return (s < 10 ? '0' : '') + s
    }
  }

  const failure = async (chatid, msg) => {
    let temp
    try {
      // eslint-disable-next-line
      let send
      [send, temp] = await Promise.all([
        bot.sendChatAction(chatid, 'typing'),
        helper.getlang(msg, logger)
      ])
      await bot.sendMessage(chatid, '📺❗ ' + temp.text(msg.chat.type, 'command.whatanime.info'), {
        reply_to_message_id: msg.message_id,
        parse_mode: 'HTML',
        reply_markup: {
          force_reply: true, selective: true
        }
      })
      logger.info('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: valid,')
    } catch (e) {
      logger.error('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: error')
      logger.debug(e.stack)
    }
  }

  const success = async (chatid, msg, photo) => {
    let temp
    try {
      logger.info('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: command received')
      // eslint-disable-next-line
      let send
      [send, temp] = await Promise.all([
        bot.sendChatAction(chatid, 'typing'),
        helper.getlang(msg, logger)
      ])

      const query = new Whatanime(global.config.apikey.whatanime)

      const url = await bot.getFileLink(photo)
      const response = await query.search(url)
      const result = response.docs[0]
      let resultMessage = ''
      if (result.anime.toLowerCase() !== result.title_english.toLowerCase()) {
        resultMessage = temp.textb(msg.chat.type, 'command.whatanime.name') + ': ' + result.title + '\n' +
          temp.textb(msg.chat.type, 'command.whatanime.english') + ': ' + result.title_english + '\n'
      } else {
        resultMessage = temp.textb(msg.chat.type, 'command.whatanime.name') + ': ' + result.anime + '\n'
      }
      const time = new Format(result.at)
      resultMessage = resultMessage +
        temp.textb(msg.chat.type, 'command.whatanime.episode') + ' ' + result.episode + '\n' +
        temp.textb(msg.chat.type, 'command.whatanime.time') + ': ' +
        (time.hour === '00' ? '' : time.hour + ' : ') + time.min + ' : ' + time.sec + '\n' +
        temp.textb(msg.chat.type, 'command.whatanime.match') + ': ' + (result.similarity * 100).toFixed(2) + '%'
      if (result.similarity * 100 < 70) {
        resultMessage = resultMessage + '\n' + temp.text(msg.chat.type, 'command.whatanime.incorrect')
        await bot.sendMessage(chatid, resultMessage, {
          parse_mode: 'HTML',
          disable_web_page_preview: true,
          reply_to_message_id: msg.message_id
        })
      } else {
        const animeVideo = await query.previewVideo(result.season, result.anime, result.filename, result.at, result.tokenthumb)
        await Promise.all([
          bot.sendMessage(chatid, resultMessage, {
            parse_mode: 'HTML',
            disable_web_page_preview: true,
            reply_to_message_id: msg.message_id
          }),
          bot.sendVideo(chatid, animeVideo, {
            reply_to_message_id: msg.message_id
          })
        ])
      }
      logger.info('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: valid')
    } catch (e) {
      logger.error('chatid: ' + chatid + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: error')
      logger.debug(e.stack)
    }
  }

  bot.on('message', async (msg) => {
    const regex1 = new RegExp('^(?:무슨애니|whatanime|anime|/(?:무슨애니|whatanime)+(?:@' + global.botinfo.username + ')? ?)$')
    const regex2 = new RegExp('/(?:무슨애니|whatanime)+(?:@' + global.botinfo.username + ')? ?$')
    try {
      if (Math.round((new Date()).getTime() / 1000) - msg.date >= 180) return
      if (msg.photo) {
        if (regex1.test(msg.caption)) {
          await success(msg.chat.id, msg, msg.photo[msg.photo.length - 1].file_id)
          return
        } else if (msg.reply_to_message && msg.reply_to_message.from &&
          msg.reply_to_message.from.username === global.botinfo.username &&
          msg.reply_to_message.text.match(/📺❗/)) {
          await success(msg.chat.id, msg, msg.photo[msg.photo.length - 1].file_id)
          return
        }
      } else {
        if (regex1.test(msg.text) &&
          msg.reply_to_message && msg.reply_to_message.photo) {
          await success(msg.chat.id, msg, msg.reply_to_message.photo[msg.reply_to_message.photo.length - 1].file_id)
          return
        } else if (regex2.test(msg.text)) {
          await failure(msg.chat.id, msg)
          return
        }
      }
    } catch (e) {
      logger.error('chatid: ' + msg.chat.id + ', username: ' + helper.getuser(msg.from) + ', lang: ' + msg.from.language_code + ', command: whatanime, type: error')
      logger.debug(e.stack)
    }
  })
}
<|endoftext|>


<commit_before>/*** Imports ***/
var papaya = papaya || {};
papaya.viewer = papaya.viewer || {};

/*** Constructor ***/
papaya.viewer.MinIP = papaya.viewer.MinIP || function (width, height, screenVols) {
        this.screenVolumes = screenVols;
        this.xDim = width;
        this.yDim = height;
    };

papaya.viewer.MinIP.getVectorLength = function (vector) {
    return Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1]);
};

papaya.viewer.MinIP.getDistanceBetweenTwoPoints = function (firstPoint, secondPoint) {
    var vectorBetweenTwoPoints = [firstPoint[0] - secondPoint[0], firstPoint[1] - secondPoint[1]];
    return papaya.viewer.MinIP.getVectorLength(vectorBetweenTwoPoints);
};

papaya.viewer.MinIP.getEndPoints = function (directionVector, pointOnLine, width, height, eye){
    var normVector = [directionVector[1], directionVector[0]*(-1)];
    var c = normVector[0]*pointOnLine[0] + normVector[1]*pointOnLine[1];
    // Array that contains the viewport's four boundary lines' intersections with the crossing line
    var hit = [];
    var epsilon = 1;

    // In the first case the line is horizontal, in the second it is vertical, in the third it is diagonal,
    // therefore the number of intersections change accordingly. The intersections can be calculated from the
    // equation of the a line that can be given from the normal vector and a point on the line
    if(Math.abs(directionVector[1]) < epsilon) {
        hit[0] = [0, pointOnLine[1]];
        hit[1] = [width, pointOnLine[1]];
    } else if (Math.abs(directionVector[0]) < epsilon) {
        hit[0] = [pointOnLine[0], 0];
        hit[1] = [pointOnLine[0], height];
    } else {
        hit[0] = [0, c/normVector[1]];
        hit[1] = [c/normVector[0], 0];
        hit[2] = [width, (c-normVector[0]*width)/normVector[1]];
        hit[3] = [(c-normVector[1]*height)/normVector[0], height];
    }

    var endPoints = [];

    // If there are two intersections the endpoints are clear, but if there is more it should be investigated
    // which two is on the boundary of the viewport
    if(hit.length === 2) { //TODO: multiple checks  not needed
        endPoints.push(hit[0]);
        endPoints.push(hit[1]);
    } else {
        for(var i = 0; i < hit.length; i++){
            if(hit[i][0] >= 0 && hit[i][0] <= width && hit[i][1] >= 0 && hit[i][1] <= height) {
                endPoints.push(hit[i]);
            }
        }
    }

    //In case the line is not crossing the slice
    if(endPoints.length < 2) {
        endPoints[0] = [0,0];
        endPoints[1] = [0,0];
    }else if(papaya.viewer.MinIP.getDistanceBetweenTwoPoints(endPoints[0], eye) > papaya.viewer.MinIP.getDistanceBetweenTwoPoints(endPoints[1], eye)) {
        endPoints.reverse(); //TODO: examine only once
    }

    return endPoints;
};

papaya.viewer.MinIP.renderMinIP = function(pixels, MinIPRotationImage, cols, rows, slices, alpha, lineLength){

    var diagonal = Math.round(Math.sqrt(rows*rows + cols*cols));
    var distanceBetweenCenterAndEye = Math.round(diagonal*0.7); //TODO: is this the optimal?
    const center = [rows/2, cols/2];
    var eye = [center[0] + Math.cos(alpha)*distanceBetweenCenterAndEye, center[1]+Math.sin(alpha)*distanceBetweenCenterAndEye];

    var pointsOnLine = new Array(lineLength+1);

    var direction = [center[0]-eye[0], center[1]-eye[1]];
    var directionLength = Math.sqrt(direction[0]*direction[0]+direction[1]*direction[1]);
    var normDirection = [direction[0]/directionLength, direction[1]/directionLength];
    var normNormalVector = [normDirection[1], -normDirection[0]];

    //recalculating points on line based on changing angle
    pointsOnLine[ Math.round(lineLength/2)] = eye;
    for(var i = 0; i < Math.round(lineLength/2); i++){
        pointsOnLine[lineLength/2+i+1] = [eye[0]+normNormalVector[0]*(i+1), eye[1]+normNormalVector[1]*(i+1)]; //TODO: adding instead of multiplication
        pointsOnLine[lineLength/2-i-1] = [eye[0]-normNormalVector[0]*(i+1), eye[1]-normNormalVector[1]*(i+1)];
    }

    var endPoints = new Array(lineLength);
    var distanceBetweenEndpoints = new Array(lineLength);
    for(var i = 0; i < lineLength; i++) {
        endPoints[i] = papaya.viewer.MinIP.getEndPoints(direction, pointsOnLine[i], rows, cols, eye);
        distanceBetweenEndpoints[i] = Math.round(Math.sqrt(Math.pow(endPoints[i][0][0] - endPoints[i][1][0], 2) + Math.pow(endPoints[i][0][1] - endPoints[i][1][1], 2)));
    }

    //calculate the lines that are needed to observed for the maximum value
    for(var oneSlice = 0; oneSlice < slices; oneSlice++){
        for(var i = 0; i < endPoints.length; i++){
            var minIntensityOnSliceInDirection = 255;
            for(var j = 0; j < distanceBetweenEndpoints[i]; j++){
                var newPointLocation=[Math.round(endPoints[i][0][0]+normDirection[0]*j),Math.round(endPoints[i][0][1]+normDirection[1]*j)]; //TODO:
                var pixelIntensity = pixels[oneSlice*rows*cols + cols*newPointLocation[0] + newPointLocation[1]]; //TODO: szorzás el?re kiszámol
                if(pixelIntensity < minIntensityOnSliceInDirection && pixelIntensity !== 0){
                    minIntensityOnSliceInDirection = pixelIntensity;
                }
            }

            MinIPRotationImage[oneSlice*lineLength+i] = minIntensityOnSliceInDirection;
        }
    }
};

papaya.viewer.MinIP.renderMinIPAsm = Module.cwrap('renderMinIP', 'number', ['number', 'number', 'number', 'number', 'number', 'number', 'number']);
minIpBuffer = 0;

papaya.viewer.MinIP.updateMinIP = function(initialAngle, mipSlice) {
    var cols = mipSlice.screenVolumes[0].volume.header.imageDimensions.cols;
    var rows = mipSlice.screenVolumes[0].volume.header.imageDimensions.rows;
    var slices = mipSlice.screenVolumes[0].volume.header.imageDimensions.slices;
    var pixels = mipSlice.screenVolumes[0].volume.imageData.data;
    console.log("Rows: " + rows + ", Cols: " + cols + ", Slices: " + slices);

    var alpha = initialAngle;
    var diagonal = Math.round(Math.sqrt(rows*rows + cols*cols));
    var lineLength =  Math.round(diagonal*1.2); //TODO: is this the optimal?

    var c = document.getElementById("myCanvas4");
    var ctx = c.getContext("2d");
    imageDataHekk = ctx.createImageData(lineLength, slices);
	ctx.canvas.width = lineLength;
	ctx.canvas.height = slices;
    c.style.width = lineLength + "px";
    c.style.height = slices + "px";
    var MinIPRotationImage = [];

    if(localStorage.getItem('asmMIP') == 'true'){
        // c++ asm.js version
        console.log("Asm.js rendering enabled");
        if(volBuffer === 0 || pixBuffer !== pixels){
			// initial load, or the volume has changed
			if(volBuffer !== 0) {
				Module._free(volBuffer);
			}
			volBuffer = Module._malloc(pixels.length*pixels.BYTES_PER_ELEMENT);
			Module.HEAPU8.set(pixels, volBuffer);

			pixBuffer = pixels;
		}

        const mipSize = lineLength*slices;
		if(mipBuffer !== 0){
			Module._free(mipBuffer);
		}
        minIpBuffer = Module._malloc(mipSize);
        console.log("volBuffer: "+volBuffer +" minIpBuffer: "+minIpBuffer);
        papaya.viewer.MinIP.renderMinIPAsm(volBuffer, minIpBuffer, cols, rows, slices, alpha, lineLength);
        for(var i = 0; i < mipSize; i++){
            MinIPRotationImage[i] = Module.HEAPU8[mipBuffer+i];
        }
    }else{
        // javascript version
        papaya.viewer.MinIP.renderMinIP(pixels, MinIPRotationImage, cols, rows, slices, alpha, lineLength);
    }

    for(var i = 0; i < MinIPRotationImage.length; i++){
        var intensity = (MinIPRotationImage[i]*MinIPRotationImage[i])/300;
        imageDataHekk.data[i*4] = intensity;
        imageDataHekk.data[i*4+1] = intensity;
        imageDataHekk.data[i*4+2] = intensity;
        imageDataHekk.data[i*4+3] = 255;
    }

    ctx.putImageData(imageDataHekk, 0, 0);

    var isMouseDown = false;

    c.addEventListener("mouseup", function (ev){
        isMouseDown = false;
    });

    c.addEventListener("mousedown", function(ev){
        isMouseDown = true;
    });

    c.addEventListener('mousemove', function(ev){
        if(isMouseDown){
            var start = performance.now();
            alpha = alpha+ev.movementX/80;
            imageDataHekk = ctx.createImageData(lineLength, slices);
            var MinIPRotationImage = [];

            var beforeRendering = performance.now();
            if(localStorage.getItem('asmMIP') == 'true'){
                // c++ asm.js version
                papaya.viewer.MinIP.renderMinIPAsm(volBuffer, minIpBuffer, cols, rows, slices, alpha, lineLength);
                const mipSize = lineLength*slices;
                for(var i=0; i < mipSize; i++){
                    MinIPRotationImage[i] = Module.HEAPU8[mipBuffer+i];
                }
            } else {
                papaya.viewer.MinIP.renderMinIP(pixels, MinIPRotationImage, cols, rows, slices, alpha, lineLength);
            }
            var afterRendering = performance.now();

            for(var i = 0; i < MinIPRotationImage.length; i++){
                var intensity = (MinIPRotationImage[i]*MinIPRotationImage[i])/300;
                imageDataHekk.data[i*4] = intensity;
                imageDataHekk.data[i*4+1] = intensity;
                imageDataHekk.data[i*4+2] = intensity;
                imageDataHekk.data[i*4+3] = 255;
            }

            ctx.putImageData(imageDataHekk, 0, 0);
            var end = performance.now();
            console.log("MinIP time: " + (afterRendering-beforeRendering) + "ms");
            console.log("Total time: " + (end-start) + "ms");
        }
    });

};<commit_msg>Update minip.js<commit_after>/*** Imports ***/
var papaya = papaya || {};
papaya.viewer = papaya.viewer || {};

/*** Constructor ***/
papaya.viewer.MinIP = papaya.viewer.MinIP || function (width, height, screenVols) {
        this.screenVolumes = screenVols;
        this.xDim = width;
        this.yDim = height;
    };

papaya.viewer.MinIP.getVectorLength = function (vector) {
    return Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1]);
};

papaya.viewer.MinIP.getDistanceBetweenTwoPoints = function (firstPoint, secondPoint) {
    var vectorBetweenTwoPoints = [firstPoint[0] - secondPoint[0], firstPoint[1] - secondPoint[1]];
    return papaya.viewer.MinIP.getVectorLength(vectorBetweenTwoPoints);
};

papaya.viewer.MinIP.getEndPoints = function (directionVector, pointOnLine, width, height, eye){
    var normVector = [directionVector[1], directionVector[0]*(-1)];
    var c = normVector[0]*pointOnLine[0] + normVector[1]*pointOnLine[1];
    // Array that contains the viewport's four boundary lines' intersections with the crossing line
    var hit = [];
    var epsilon = 1;

    // In the first case the line is horizontal, in the second it is vertical, in the third it is diagonal,
    // therefore the number of intersections change accordingly. The intersections can be calculated from the
    // equation of the a line that can be given from the normal vector and a point on the line
    if(Math.abs(directionVector[1]) < epsilon) {
        hit[0] = [0, pointOnLine[1]];
        hit[1] = [width, pointOnLine[1]];
    } else if (Math.abs(directionVector[0]) < epsilon) {
        hit[0] = [pointOnLine[0], 0];
        hit[1] = [pointOnLine[0], height];
    } else {
        hit[0] = [0, c/normVector[1]];
        hit[1] = [c/normVector[0], 0];
        hit[2] = [width, (c-normVector[0]*width)/normVector[1]];
        hit[3] = [(c-normVector[1]*height)/normVector[0], height];
    }

    var endPoints = [];

    // If there are two intersections the endpoints are clear, but if there is more it should be investigated
    // which two is on the boundary of the viewport
    if(hit.length === 2) { //TODO: multiple checks  not needed
        endPoints.push(hit[0]);
        endPoints.push(hit[1]);
    } else {
        for(var i = 0; i < hit.length; i++){
            if(hit[i][0] >= 0 && hit[i][0] <= width && hit[i][1] >= 0 && hit[i][1] <= height) {
                endPoints.push(hit[i]);
            }
        }
    }

    //In case the line is not crossing the slice
    if(endPoints.length < 2) {
        endPoints[0] = [0,0];
        endPoints[1] = [0,0];
    }else if(papaya.viewer.MinIP.getDistanceBetweenTwoPoints(endPoints[0], eye) > papaya.viewer.MinIP.getDistanceBetweenTwoPoints(endPoints[1], eye)) {
        endPoints.reverse(); //TODO: examine only once
    }

    return endPoints;
};

papaya.viewer.MinIP.renderMinIP = function(pixels, MinIPRotationImage, cols, rows, slices, alpha, lineLength){

    var diagonal = Math.round(Math.sqrt(rows*rows + cols*cols));
    var distanceBetweenCenterAndEye = Math.round(diagonal*0.7); //TODO: is this the optimal?
    const center = [rows/2, cols/2];
    var eye = [center[0] + Math.cos(alpha)*distanceBetweenCenterAndEye, center[1]+Math.sin(alpha)*distanceBetweenCenterAndEye];

    var pointsOnLine = new Array(lineLength+1);

    var direction = [center[0]-eye[0], center[1]-eye[1]];
    var directionLength = Math.sqrt(direction[0]*direction[0]+direction[1]*direction[1]);
    var normDirection = [direction[0]/directionLength, direction[1]/directionLength];
    var normNormalVector = [normDirection[1], -normDirection[0]];

    //recalculating points on line based on changing angle
    pointsOnLine[ Math.round(lineLength/2)] = eye;
    for(var i = 0; i < Math.round(lineLength/2); i++){
        pointsOnLine[lineLength/2+i+1] = [eye[0]+normNormalVector[0]*(i+1), eye[1]+normNormalVector[1]*(i+1)]; //TODO: adding instead of multiplication
        pointsOnLine[lineLength/2-i-1] = [eye[0]-normNormalVector[0]*(i+1), eye[1]-normNormalVector[1]*(i+1)];
    }

    var endPoints = new Array(lineLength);
    var distanceBetweenEndpoints = new Array(lineLength);
    for(var i = 0; i < lineLength; i++) {
        endPoints[i] = papaya.viewer.MinIP.getEndPoints(direction, pointsOnLine[i], rows, cols, eye);
        distanceBetweenEndpoints[i] = Math.round(Math.sqrt(Math.pow(endPoints[i][0][0] - endPoints[i][1][0], 2) + Math.pow(endPoints[i][0][1] - endPoints[i][1][1], 2)));
    }

    //calculate the lines that are needed to observed for the maximum value
    for(var oneSlice = 0; oneSlice < slices; oneSlice++){
        for(var i = 0; i < endPoints.length; i++){
            var minIntensityOnSliceInDirection = 255;
            for(var j = 0; j < distanceBetweenEndpoints[i]; j++){
                var newPointLocation=[Math.round(endPoints[i][0][0]+normDirection[0]*j),Math.round(endPoints[i][0][1]+normDirection[1]*j)]; //TODO:
                var pixelIntensity = pixels[oneSlice*rows*cols + cols*newPointLocation[0] + newPointLocation[1]]; //TODO: szorzás el?re kiszámol
                if(pixelIntensity < minIntensityOnSliceInDirection && pixelIntensity !== 0){
                    minIntensityOnSliceInDirection = pixelIntensity;
                }
            }

            MinIPRotationImage[oneSlice*lineLength+i] = minIntensityOnSliceInDirection;
        }
    }
};

papaya.viewer.MinIP.renderMinIPAsm = Module.cwrap('renderMinIP', 'number', ['number', 'number', 'number', 'number', 'number', 'number', 'number']);
minIpBuffer = 0;

papaya.viewer.MinIP.updateMinIP = function(initialAngle, mipSlice) {
    var cols = mipSlice.screenVolumes[0].volume.header.imageDimensions.cols;
    var rows = mipSlice.screenVolumes[0].volume.header.imageDimensions.rows;
    var slices = mipSlice.screenVolumes[0].volume.header.imageDimensions.slices;
    var pixels = mipSlice.screenVolumes[0].volume.imageData.data;
    console.log("Rows: " + rows + ", Cols: " + cols + ", Slices: " + slices);

    var alpha = initialAngle;
    var diagonal = Math.round(Math.sqrt(rows*rows + cols*cols));
    var lineLength =  Math.round(diagonal*1.2); //TODO: is this the optimal?

    var c = document.getElementById("myCanvas4");
    var ctx = c.getContext("2d");
    imageDataHekk = ctx.createImageData(lineLength, slices);
	ctx.canvas.width = lineLength;
	ctx.canvas.height = slices;
    c.style.width = lineLength + "px";
    c.style.height = slices + "px";
    var MinIPRotationImage = [];

	if(volBuffer === 0 || pixBuffer !== pixels){
			// initial load, or the volume has changed
			if(volBuffer !== 0) {
				Module._free(volBuffer);
			}
			volBuffer = Module._malloc(pixels.length*pixels.BYTES_PER_ELEMENT);
			Module.HEAPU8.set(pixels, volBuffer);

			pixBuffer = pixels;
		}

        const mipSize = lineLength*slices;
		if(mipBuffer !== 0){
			Module._free(mipBuffer);
		}
        minIpBuffer = Module._malloc(mipSize);

    if(localStorage.getItem('asmMIP') == 'true'){
        // c++ asm.js version
        console.log("Asm.js rendering enabled");

        console.log("volBuffer: "+volBuffer +" minIpBuffer: "+minIpBuffer);
        papaya.viewer.MinIP.renderMinIPAsm(volBuffer, minIpBuffer, cols, rows, slices, alpha, lineLength);
        for(var i = 0; i < mipSize; i++){
            MinIPRotationImage[i] = Module.HEAPU8[mipBuffer+i];
        }
    }else{
        // javascript version
        papaya.viewer.MinIP.renderMinIP(pixels, MinIPRotationImage, cols, rows, slices, alpha, lineLength);
    }

    for(var i = 0; i < MinIPRotationImage.length; i++){
        var intensity = (MinIPRotationImage[i]*MinIPRotationImage[i])/300;
        imageDataHekk.data[i*4] = intensity;
        imageDataHekk.data[i*4+1] = intensity;
        imageDataHekk.data[i*4+2] = intensity;
        imageDataHekk.data[i*4+3] = 255;
    }

    ctx.putImageData(imageDataHekk, 0, 0);

    var isMouseDown = false;

    c.addEventListener("mouseup", function (ev){
        isMouseDown = false;
    });

    c.addEventListener("mousedown", function(ev){
        isMouseDown = true;
    });

    c.addEventListener('mousemove', function(ev){
        if(isMouseDown){
            var start = performance.now();
            alpha = alpha+ev.movementX/80;
            imageDataHekk = ctx.createImageData(lineLength, slices);
            var MinIPRotationImage = [];

            var beforeRendering = performance.now();
            if(localStorage.getItem('asmMIP') == 'true'){
                // c++ asm.js version
                papaya.viewer.MinIP.renderMinIPAsm(volBuffer, minIpBuffer, cols, rows, slices, alpha, lineLength);
                const mipSize = lineLength*slices;
                for(var i=0; i < mipSize; i++){
                    MinIPRotationImage[i] = Module.HEAPU8[mipBuffer+i];
                }
            } else {
                papaya.viewer.MinIP.renderMinIP(pixels, MinIPRotationImage, cols, rows, slices, alpha, lineLength);
            }
            var afterRendering = performance.now();

            for(var i = 0; i < MinIPRotationImage.length; i++){
                var intensity = (MinIPRotationImage[i]*MinIPRotationImage[i])/300;
                imageDataHekk.data[i*4] = intensity;
                imageDataHekk.data[i*4+1] = intensity;
                imageDataHekk.data[i*4+2] = intensity;
                imageDataHekk.data[i*4+3] = 255;
            }

            ctx.putImageData(imageDataHekk, 0, 0);
            var end = performance.now();
            console.log("MinIP time: " + (afterRendering-beforeRendering) + "ms");
            console.log("Total time: " + (end-start) + "ms");
        }
    });

};
<|endoftext|>


<commit_before>/* global HTMLSlotElement */

import WebComponent from './web-component'
import MotorHTMLNode from './node'
import { observeChildren, getShadowRootVersion, hasShadowDomV0, hasShadowDomV1,
    getAncestorShadowRootIfAny } from '../motor/Utility'

var DeclarativeBase

// Override HTMLElement.prototype.attachShadow in v1, and
// HTMLElement.prototype.createShadowRoot in v0, so that we can make a Map of
// motor- elements to their shadow roots, so we can always get a reference to
// the element's shadow root even if it is closed.
const observers = new WeakMap
function hijack(original) {
    return function(...args) {
        // In v0, shadow roots can be replaced, but in v1 calling attachShadow
        // on an element that already has a root throws. So, we can set this to
        // true, and if the try-catch passes then we know we have a v0 root and
        // that the root was just replaced.
        const oldRoot = this.shadowRoot
        let root = null
        try {
            root = original.call(this, ...args)
        }
        catch (e) { throw e }
        if (this instanceof DeclarativeBase) {
            this._hasShadowRoot = true
            if (oldRoot) {
                onV0ShadowRootReplaced.call(this, oldRoot)
            }
            const observer = observeChildren(root, shadowRootChildAdded.bind(this), shadowRootChildRemoved.bind(this))
            observers.set(root, observer)

            for (const child of this.children) {
                if (!(child instanceof DeclarativeBase)) continue
                child._isPossiblyDistributed = true
            }
        }
        return root
    }
}
function shadowRootChildAdded(child) {
    if (!(child instanceof DeclarativeBase)) return
    this.imperativeCounterpart.addChild(child.imperativeCounterpart)
}
function shadowRootChildRemoved(child) {
    if (!(child instanceof DeclarativeBase)) return
    this.imperativeCounterpart.removeChild(child.imperativeCounterpart)
}
function onV0ShadowRootReplaced(oldRoot) {
    observers.get(oldRoot).disconnect()
    observers.delete(oldRoot)
    let i = 0
    for (let child of oldRoot.childNodes) {
        if (!(child instanceof DeclarativeBase)) { i += 1; continue }

        // We should disconnect the imperative connection (f.e. so it is
        // not rendered in WebGL)...
        this.imperativeCounterpart.removeChild(child.imperativeCounterpart)

        // ...but we should place the element back where it was so there's
        // no surprises to the HTML-API user who might go looking for the
        // element. Due to the fact that the observer on the oldRoot was
        // removed, adding the element back to the oldRoot won't cause it
        // to be reconnected on the imperative side.
        oldRoot.insertBefore(child, oldRoot.childNodes[i])

        i += 1
    }
}
if (HTMLElement.prototype.createShadowRoot instanceof Function)
    HTMLElement.prototype.createShadowRoot = hijack(HTMLElement.prototype.createShadowRoot)
if (HTMLElement.prototype.attachShadow instanceof Function)
    HTMLElement.prototype.attachShadow = hijack(HTMLElement.prototype.attachShadow)

// ... Little did I know that the `WebComponent` function I made is
// considered a form of mixin. ...
// TODO: follow the mixin pattern as with Node and Scene classes.

initMotorHTMLBase()
export function initMotorHTMLBase() {
    if (DeclarativeBase) return

    /**
     * @implements {EventListener}
     */
    DeclarativeBase = class DeclarativeBase extends WebComponent(window.HTMLElement) {
        createdCallback() {
            super.createdCallback()

            this.imperativeCounterpart = null // to hold the imperative API Node instance.

            // true if this node has a shadow root (even if it is "closed", see
            // hijack function above). Once true always true because shadow
            // roots cannot be removed.
            this._hasShadowRoot = false

            // True when this node has a parent that has a shadow root. When
            // using the HTML API, Imperative API can look at this to determine
            // whether to render this node or not, in the case of WebGL.
            this._isPossiblyDistributed = false

            // A map of the slot elements that are children of this node and
            // their last-known assigned nodes. When a slotchange happens while
            // this node is in a shadow root and has a slot child, we can
            // detect what the difference is between the last known and the new
            // assignments, and notate the new distribution of child nodes. See
            // issue #40 for background on why we do this.
            this._slotElementsAssignedNodes = new WeakMap

            // If this node is distributed into a shadow tree, this will
            // reference the parent of the <slot> or <content> element.
            // Basically, this node will render as a child of that parent node
            // in the flat tree.
            this._shadowParent = null

            // If this element has a child <slot> or <content> element while in
            // a shadow root, then this will be a Set of the nodes distributed
            // into the <slot> or <content>, and those nodes render relatively
            // to this node in the flat tree. We instantiate this later, only
            // when/if needed.
            this._shadowChildren = null

            // XXX: "this.ready" seems to be more intuitive on the HTML side than
            // "this.mountPromise" because if the user has a reference to a
            // motor-node or a motor-scene and it exists in DOM, then it is already
            // "mounted" from the HTML API perspective although not necessarily
            // ready because `connectedCallback`. Maybe we can use "mountPromise"
            // for the imperative API, and "ready" for the HTML API. For example:
            //
            // await $('motor-scene')[0].ready // When using the HTML API
            // await node.mountPromise // When using the imperative API
            //
            // Or, maybe we can just use ".ready" in both APIs?...
            this._resolveReadyPromise = null
            this.ready = new Promise(r => this._resolveReadyPromise = r)
        }

        // called by WebComponent#connectedCallback()
        init() {

            // XXX: we call this._associateImperativeNode() before super.init() because
            // super.init() may call this.childConnectedCallback() which depends
            // on the imperative counterpart existing.
            this._associateImperativeNode()

            super.init()
        }

        /**
         * This method creates the association between this MotorHTMLNode instance
         * and the imperative Node instance.
         *
         * This method may get called by this.init, but can also be called by
         * the Node class if Node is used imperatively. See Node#constructor.
         *
         * @private
         *
         * @param {Object} imperativeCounterpart The imperative counterpart to
         * associate with this MotorHTML element. This parameter is only used in the
         * imperative API constructors, and this happens when using the imperative
         * form of infamous instead of the HTML interface to infamous. When the HTML
         * interface is used, this gets called first without an
         * imperativeCounterpart argument and the call to this in an imperative
         * constructor will be a noop. Basically, either this gets called first by a
         * MotorHTML element, or first by an imperative instance, depending on which
         * API is used first.
         */
        _associateImperativeNode(imperativeCounterpart) {
            // if the association is made already, noop
            if (this.imperativeCounterpart) return

            // if called from an imperative-side class' constructor, associate
            // the passed instance.
            if (imperativeCounterpart) this.imperativeCounterpart = imperativeCounterpart

            // otherwise if called from a MotorHTML class without an argument
            else this.imperativeCounterpart = this._makeImperativeCounterpart()

            this._signalWhenReady()
        }

        /**
         * This method should be overriden by child classes. It should return the
         * imperative-side instance that the HTML-side class (this) corresponds to.
         * @abstract
         */
        _makeImperativeCounterpart() {
            throw new TypeError('This method should be implemented by classes extending DeclarativeBase.')
        }

        async _signalWhenReady() {
            await this.imperativeCounterpart.mountPromise
            this._resolveReadyPromise()
        }

        childConnectedCallback(child) {

            // mirror the DOM connections in the imperative API's virtual scene graph.
            if (child instanceof MotorHTMLNode) {
                if (this._hasShadowRoot) child._isPossiblyDistributed = true
                this.imperativeCounterpart.addChild(child.imperativeCounterpart)
            }

            // TODO: There's no way currently to detect v0 vs v1 ShadowRoots,
            // so these conditional checks assume the end user is using
            // <content> elements in v0 roots, and <slot> elements in v1 roots.
            // Doing otherwise may have unknown and undersirable behavior. See
            // the TODO for getShadowRootVersion in ../motor/Utility for more
            // info.
            else if (
                hasShadowDomV0
                && child instanceof HTMLContentElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v0'
            ) {
                // TODO observe <content> elements.
            }
            else if (
                hasShadowDomV1
                && child instanceof HTMLSlotElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v1'
            ) {
                child.addEventListener('slotchange', this)
                this._handleDistributedChildren(child)
            }
        }

        // This method is part of the EventListener interface.
        handleEvent(event) {
            if (event.type == 'slotchange') {
                const slot = event.target
                this._handleDistributedChildren(slot)
            }
        }

        _handleDistributedChildren(slot) {
            const diff = this._getDistributedChildDifference(slot)

            for (const addedNode of diff.added) {
                if (!(addedNode instanceof DeclarativeBase)) continue

                // We do this because if the given slot is assigned to another
                // slot, then this logic will run again for the next slot on
                // that next slot's slotchange, so we remove the distributed
                // node from the previous shadowParent and add it to the next
                // one. If we don't do this, then the distributed node will
                // exist in multiple shadowChildren lists when there is a
                // chain of assigned slots. For more info, see
                // https://github.com/w3c/webcomponents/issues/611
                if (addedNode._shadowParent && addedNode._shadowParent._shadowChildren) {
                    addedNode._shadowParent._shadowChildren.delete(addedNode)
                    if (!addedNode._shadowParent._shadowChildren.size)
                        addedNode._shadowParent._shadowChildren = null
                }

                addedNode._shadowParent = this
                if (!this._shadowChildren) this._shadowChildren = new Set
                this._shadowChildren.add(addedNode)
            }

            for (const removedNode of diff.removed) {
                if (!(removedNode instanceof DeclarativeBase)) continue
                removedNode._shadowParent = null
                this._shadowChildren.delete(removedNode)
                if (!this._shadowChildren.size) this._shadowChildren = null
            }
        }

        _getDistributedChildDifference(slot) {
            let previousNodes

            if (this._slotElementsAssignedNodes.has(slot))
                previousNodes = this._slotElementsAssignedNodes.get(slot)
            else
                previousNodes = []

            const newNodes = slot.assignedNodes({flatten: true})

            // save the newNodes to be used as the previousNodes for next time.
            this._slotElementsAssignedNodes.set(slot, newNodes)

            const diff = {
                removed: [],
            }

            for (let i=0, l=previousNodes.length; i<l; i+=1) {
                let oldNode = previousNodes[i]
                let newIndex = newNodes.indexOf(oldNode)

                // if it exists in the previousNodes but not the newNodes, then
                // the node was removed.
                if (!(newIndex >= 0)) {
                    diff.removed.push(oldNode)
                }

                // otherwise the node wasn't added or removed.
                else {
                    newNodes.splice(i, 1)
                }
            }

            // Remaining nodes in newNodes must have been added.
            diff.added = newNodes

            return diff
        }

        childDisconnectedCallback(child) {
            // mirror the connection in the imperative API's virtual scene graph.
            if (child instanceof MotorHTMLNode) {
                child._isPossiblyDistributed = false
                this.imperativeCounterpart.removeChild(child.imperativeCounterpart)
            }
            else if (
                hasShadowDomV0
                && child instanceof HTMLContentElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v0'
            ) {
                // TODO: unobserve <content> element
            }
            else if (
                hasShadowDomV1
                && child instanceof HTMLSlotElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v1'
            ) {
                child.removeEventListener('slotchange', this)
                this._handleDistributedChildren(child)
                this._slotElementsAssignedNodes.delete(child)
            }
        }
    }
}

export {DeclarativeBase as default}
<commit_msg>Watch top-most slot elements in a ShadowRoot. That case was not handled previously, so HTML-API elements distributed to those top level slot elements were not being tracked as shadowChildren of the shadow root host.<commit_after>/* global HTMLSlotElement */

import WebComponent from './web-component'
import MotorHTMLNode from './node'
import { observeChildren, getShadowRootVersion, hasShadowDomV0, hasShadowDomV1,
    getAncestorShadowRootIfAny } from '../motor/Utility'

var DeclarativeBase

// Override HTMLElement.prototype.attachShadow in v1, and
// HTMLElement.prototype.createShadowRoot in v0, so that we can make a Map of
// motor- elements to their shadow roots, so we can always get a reference to
// the element's shadow root even if it is closed.
const observers = new WeakMap
function hijack(original) {
    return function(...args) {
        // In v0, shadow roots can be replaced, but in v1 calling attachShadow
        // on an element that already has a root throws. So, we can set this to
        // true, and if the try-catch passes then we know we have a v0 root and
        // that the root was just replaced.
        const oldRoot = this.shadowRoot
        let root = null
        try {
            root = original.call(this, ...args)
        }
        catch (e) { throw e }
        if (this instanceof DeclarativeBase) {
            this._hasShadowRoot = true
            if (oldRoot) {
                onV0ShadowRootReplaced.call(this, oldRoot)
            }
            const observer = observeChildren(root, shadowRootChildAdded.bind(this), shadowRootChildRemoved.bind(this))
            observers.set(root, observer)

            for (const child of this.children) {
                if (!(child instanceof DeclarativeBase)) continue
                child._isPossiblyDistributed = true
            }
        }
        return root
    }
}
function shadowRootChildAdded(child) {

    // XXX Logic here is similar to childConnectedCallback

    if (child instanceof DeclarativeBase) {
        this.imperativeCounterpart.addChild(child.imperativeCounterpart)
    }
    else if (
        hasShadowDomV0
        && child instanceof HTMLContentElement
    ) {
        // TODO observe <content> elements.
    }
    else if (
        hasShadowDomV1
        && child instanceof HTMLSlotElement
    ) {
        child.addEventListener('slotchange', this)
        this._handleDistributedChildren(child)
    }
}
function shadowRootChildRemoved(child) {

    // XXX Logic here is similar to childDisconnectedCallback

    if (child instanceof DeclarativeBase) {
        this.imperativeCounterpart.removeChild(child.imperativeCounterpart)
    }
    else if (
        hasShadowDomV0
        && child instanceof HTMLContentElement
    ) {
        // TODO: unobserve <content> element
    }
    else if (
        hasShadowDomV1
        && child instanceof HTMLSlotElement
    ) {
        child.removeEventListener('slotchange', this)
        this._handleDistributedChildren(child)
        this._slotElementsAssignedNodes.delete(child)
    }
}
function onV0ShadowRootReplaced(oldRoot) {
    observers.get(oldRoot).disconnect()
    observers.delete(oldRoot)
    let i = 0
    for (let child of oldRoot.childNodes) {
        if (!(child instanceof DeclarativeBase)) { i += 1; continue }

        // We should disconnect the imperative connection (f.e. so it is
        // not rendered in WebGL)...
        this.imperativeCounterpart.removeChild(child.imperativeCounterpart)

        // ...but we should place the element back where it was so there's
        // no surprises to the HTML-API user who might go looking for the
        // element. Due to the fact that the observer on the oldRoot was
        // removed, adding the element back to the oldRoot won't cause it
        // to be reconnected on the imperative side.
        oldRoot.insertBefore(child, oldRoot.childNodes[i])

        i += 1
    }
}
if (HTMLElement.prototype.createShadowRoot instanceof Function)
    HTMLElement.prototype.createShadowRoot = hijack(HTMLElement.prototype.createShadowRoot)
if (HTMLElement.prototype.attachShadow instanceof Function)
    HTMLElement.prototype.attachShadow = hijack(HTMLElement.prototype.attachShadow)

// ... Little did I know that the `WebComponent` function I made is
// considered a form of mixin. ...
// TODO: follow the mixin pattern as with Node and Scene classes.

initMotorHTMLBase()
export function initMotorHTMLBase() {
    if (DeclarativeBase) return

    /**
     * @implements {EventListener}
     */
    DeclarativeBase = class DeclarativeBase extends WebComponent(window.HTMLElement) {
        createdCallback() {
            super.createdCallback()

            this.imperativeCounterpart = null // to hold the imperative API Node instance.

            // true if this node has a shadow root (even if it is "closed", see
            // hijack function above). Once true always true because shadow
            // roots cannot be removed.
            this._hasShadowRoot = false

            // True when this node has a parent that has a shadow root. When
            // using the HTML API, Imperative API can look at this to determine
            // whether to render this node or not, in the case of WebGL.
            this._isPossiblyDistributed = false

            // A map of the slot elements that are children of this node and
            // their last-known assigned nodes. When a slotchange happens while
            // this node is in a shadow root and has a slot child, we can
            // detect what the difference is between the last known and the new
            // assignments, and notate the new distribution of child nodes. See
            // issue #40 for background on why we do this.
            this._slotElementsAssignedNodes = new WeakMap

            // If this node is distributed into a shadow tree, this will
            // reference the parent of the <slot> or <content> element.
            // Basically, this node will render as a child of that parent node
            // in the flat tree.
            this._shadowParent = null

            // If this element has a child <slot> or <content> element while in
            // a shadow root, then this will be a Set of the nodes distributed
            // into the <slot> or <content>, and those nodes render relatively
            // to this node in the flat tree. We instantiate this later, only
            // when/if needed.
            this._shadowChildren = null

            // XXX: "this.ready" seems to be more intuitive on the HTML side than
            // "this.mountPromise" because if the user has a reference to a
            // motor-node or a motor-scene and it exists in DOM, then it is already
            // "mounted" from the HTML API perspective although not necessarily
            // ready because `connectedCallback`. Maybe we can use "mountPromise"
            // for the imperative API, and "ready" for the HTML API. For example:
            //
            // await $('motor-scene')[0].ready // When using the HTML API
            // await node.mountPromise // When using the imperative API
            //
            // Or, maybe we can just use ".ready" in both APIs?...
            this._resolveReadyPromise = null
            this.ready = new Promise(r => this._resolveReadyPromise = r)
        }

        // called by WebComponent#connectedCallback()
        init() {

            // XXX: we call this._associateImperativeNode() before super.init() because
            // super.init() may call this.childConnectedCallback() which depends
            // on the imperative counterpart existing.
            this._associateImperativeNode()

            super.init()
        }

        /**
         * This method creates the association between this MotorHTMLNode instance
         * and the imperative Node instance.
         *
         * This method may get called by this.init, but can also be called by
         * the Node class if Node is used imperatively. See Node#constructor.
         *
         * @private
         *
         * @param {Object} imperativeCounterpart The imperative counterpart to
         * associate with this MotorHTML element. This parameter is only used in the
         * imperative API constructors, and this happens when using the imperative
         * form of infamous instead of the HTML interface to infamous. When the HTML
         * interface is used, this gets called first without an
         * imperativeCounterpart argument and the call to this in an imperative
         * constructor will be a noop. Basically, either this gets called first by a
         * MotorHTML element, or first by an imperative instance, depending on which
         * API is used first.
         */
        _associateImperativeNode(imperativeCounterpart) {
            // if the association is made already, noop
            if (this.imperativeCounterpart) return

            // if called from an imperative-side class' constructor, associate
            // the passed instance.
            if (imperativeCounterpart) this.imperativeCounterpart = imperativeCounterpart

            // otherwise if called from a MotorHTML class without an argument
            else this.imperativeCounterpart = this._makeImperativeCounterpart()

            this._signalWhenReady()
        }

        /**
         * This method should be overriden by child classes. It should return the
         * imperative-side instance that the HTML-side class (this) corresponds to.
         * @abstract
         */
        _makeImperativeCounterpart() {
            throw new TypeError('This method should be implemented by classes extending DeclarativeBase.')
        }

        async _signalWhenReady() {
            await this.imperativeCounterpart.mountPromise
            this._resolveReadyPromise()
        }

        childConnectedCallback(child) {

            // mirror the DOM connections in the imperative API's virtual scene graph.
            if (child instanceof MotorHTMLNode) {
                if (this._hasShadowRoot) child._isPossiblyDistributed = true
                this.imperativeCounterpart.addChild(child.imperativeCounterpart)
            }

            // TODO: There's no way currently to detect v0 vs v1 ShadowRoots,
            // so these conditional checks assume the end user is using
            // <content> elements in v0 roots, and <slot> elements in v1 roots.
            // Doing otherwise may have unknown and undersirable behavior. See
            // the TODO for getShadowRootVersion in ../motor/Utility for more
            // info.
            else if (
                hasShadowDomV0
                && child instanceof HTMLContentElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v0'
            ) {
                // TODO observe <content> elements.
            }
            else if (
                hasShadowDomV1
                && child instanceof HTMLSlotElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v1'
            ) {
                child.addEventListener('slotchange', this)
                this._handleDistributedChildren(child)
            }
        }

        // This method is part of the EventListener interface.
        handleEvent(event) {
            if (event.type == 'slotchange') {
                const slot = event.target
                this._handleDistributedChildren(slot)
            }
        }

        _handleDistributedChildren(slot) {
            const diff = this._getDistributedChildDifference(slot)

            for (const addedNode of diff.added) {
                if (!(addedNode instanceof DeclarativeBase)) continue

                // We do this because if the given slot is assigned to another
                // slot, then this logic will run again for the next slot on
                // that next slot's slotchange, so we remove the distributed
                // node from the previous shadowParent and add it to the next
                // one. If we don't do this, then the distributed node will
                // exist in multiple shadowChildren lists when there is a
                // chain of assigned slots. For more info, see
                // https://github.com/w3c/webcomponents/issues/611
                if (addedNode._shadowParent && addedNode._shadowParent._shadowChildren) {
                    addedNode._shadowParent._shadowChildren.delete(addedNode)
                    if (!addedNode._shadowParent._shadowChildren.size)
                        addedNode._shadowParent._shadowChildren = null
                }

                addedNode._shadowParent = this
                if (!this._shadowChildren) this._shadowChildren = new Set
                this._shadowChildren.add(addedNode)
            }

            for (const removedNode of diff.removed) {
                if (!(removedNode instanceof DeclarativeBase)) continue
                removedNode._shadowParent = null
                this._shadowChildren.delete(removedNode)
                if (!this._shadowChildren.size) this._shadowChildren = null
            }
        }

        _getDistributedChildDifference(slot) {
            let previousNodes

            if (this._slotElementsAssignedNodes.has(slot))
                previousNodes = this._slotElementsAssignedNodes.get(slot)
            else
                previousNodes = []

            const newNodes = slot.assignedNodes({flatten: true})

            // save the newNodes to be used as the previousNodes for next time.
            this._slotElementsAssignedNodes.set(slot, newNodes)

            const diff = {
                removed: [],
            }

            for (let i=0, l=previousNodes.length; i<l; i+=1) {
                let oldNode = previousNodes[i]
                let newIndex = newNodes.indexOf(oldNode)

                // if it exists in the previousNodes but not the newNodes, then
                // the node was removed.
                if (!(newIndex >= 0)) {
                    diff.removed.push(oldNode)
                }

                // otherwise the node wasn't added or removed.
                else {
                    newNodes.splice(i, 1)
                }
            }

            // Remaining nodes in newNodes must have been added.
            diff.added = newNodes

            return diff
        }

        childDisconnectedCallback(child) {
            // mirror the connection in the imperative API's virtual scene graph.
            if (child instanceof MotorHTMLNode) {
                child._isPossiblyDistributed = false
                this.imperativeCounterpart.removeChild(child.imperativeCounterpart)
            }
            else if (
                hasShadowDomV0
                && child instanceof HTMLContentElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v0'
            ) {
                // TODO: unobserve <content> element
            }
            else if (
                hasShadowDomV1
                && child instanceof HTMLSlotElement
                &&
                //getShadowRootVersion(
                    getAncestorShadowRootIfAny(this)
                //) == 'v1'
            ) {
                child.removeEventListener('slotchange', this)
                this._handleDistributedChildren(child)
                this._slotElementsAssignedNodes.delete(child)
            }
        }
    }
}

export {DeclarativeBase as default}
<|endoftext|>


<commit_before>import { createStore, applyMiddleware, combineReducers, compose } from 'redux'

import { keaReducer } from '../reducer'
import { globalPlugins, activatePlugin } from '../plugins'

const reduxDevToolsCompose = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose

const defaultOptions = {
  paths: ['kea', 'scenes'],
  reducers: {},
  preloadedState: undefined,
  middleware: [],
  compose: reduxDevToolsCompose,
  enhancers: [],
  plugins: []
}

export function getStore (opts = {}) {
  // clone options
  let options = Object.assign({}, defaultOptions, opts)

  // activate all the global plugins
  options.plugins.forEach(plugin => {
    activatePlugin(plugin)
  })

  // clone reducers
  options.reducers = Object.assign({}, options.reducers)
  options.paths.forEach(path => {
    options.reducers[path] = keaReducer(path)
  })

  // run pre-hooks
  globalPlugins.beforeReduxStore.forEach(f => f(options))

  // combine middleware into the first enhancer
  if (options.middleware.length > 0) {
    options.enhancers = [applyMiddleware(...options.middleware)].concat(options.enhancers)
  }

  // use a special compose function?
  const composeEnchancer = options.compose || compose

  // create the store creator
  const finalCreateStore = composeEnchancer(...options.enhancers)(createStore)

  // combine reducers
  const combinedReducers = combineReducers(options.reducers)

  // create store
  const store = finalCreateStore(combinedReducers, {...options.preloadedState})

  // run post-hooks
  globalPlugins.afterReduxStore.forEach(f => f(options, store))

  return store
}
<commit_msg>object.assign<commit_after>import { createStore, applyMiddleware, combineReducers, compose } from 'redux'

import { keaReducer } from '../reducer'
import { globalPlugins, activatePlugin } from '../plugins'

const reduxDevToolsCompose = typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose

const defaultOptions = {
  paths: ['kea', 'scenes'],
  reducers: {},
  preloadedState: undefined,
  middleware: [],
  compose: reduxDevToolsCompose,
  enhancers: [],
  plugins: []
}

export function getStore (opts = {}) {
  // clone options
  let options = Object.assign({}, defaultOptions, opts)

  // activate all the global plugins
  options.plugins.forEach(plugin => {
    activatePlugin(plugin)
  })

  // clone reducers
  options.reducers = Object.assign({}, options.reducers)
  options.paths.forEach(path => {
    options.reducers[path] = keaReducer(path)
  })

  // run pre-hooks
  globalPlugins.beforeReduxStore.forEach(f => f(options))

  // combine middleware into the first enhancer
  if (options.middleware.length > 0) {
    options.enhancers = [applyMiddleware(...options.middleware)].concat(options.enhancers)
  }

  // use a special compose function?
  const composeEnchancer = options.compose || compose

  // create the store creator
  const finalCreateStore = composeEnchancer(...options.enhancers)(createStore)

  // combine reducers
  const combinedReducers = combineReducers(options.reducers)

  // create store
  const store = finalCreateStore(combinedReducers, Object.assign({}, options.preloadedState))

  // run post-hooks
  globalPlugins.afterReduxStore.forEach(f => f(options, store))

  return store
}
<|endoftext|>


<commit_before>var map                  = require('../object/map')
var omit                 = require('../object/omit')
var extend               = require('../object/extend')
var hasOwn               = require('../object/hasOwn')
var isFunction           = require('./isFunction')
var isPlainObject        = require('./isPlainObject')
var isDataDescriptor     = require('./isDescriptorData')
var isAccessorDescriptor = require('./isDescriptorAccessor')

// This method allows precise addition to or modification of a property on an object.

// Normal property addition through assignment creates properties which show up during property enumeration (for...in loop or Object.keys method), whose values may be changed, and which may be deleted. This method allows these extra details to be changed from their defaults.

// By default, values added using Object.defineProperty() are immutable.

// Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A descriptor must be one of these two flavors; it cannot be both.
// Both data and accessor descriptors are objects.

// They share the following required keys: configurable, enumerable

// A data descriptor is a property that has a value, which may or may not be writable.

// An accessor descriptor is a property described by a getter-setter pair of functions.

// This is extrememly raw.
// obj: [object] object to be mutated
// prop: [String] property to add.
// config: [Boolean|Object] boolean for configurable key or descriptor object
// enumerable: [Boolean] boolean for enumerable key
// a: [Boolean|Function] boolean if data-descriptor (alias for writable key), function if accessor-descriptor (alias for getter function)
// b: [AnyValue|Function] a javascript value if data-descriptor (alias for value), function if accessor-descriptor (alias for setter function)

function defineProperty (obj, prop, config, enumerable, a, b) {
  // multiple definition
  if (isPlainObject(prop)) {
    return Object.defineProperties(obj, map(prop, definePropDefaults))
  }
  // implicit definition
  if (isPlainObject(config)) {
    return Object.defineProperty(obj, prop, definePropDefaults(config))
  }

  // explicit definition
  var configuration = {}
  configuration.configurable = !!config || false
  configuration.enumerable = !!enumerable || false

  if (isFunction(a)) {
    configuration.get = a
    configuration.set = b
  } else {
    configuration.writable = !!a || false
    configuration.value = b
  }

  return Object.defineProperty(obj, prop, configuration)
}

function definePropDefaults (obj) {
  var options = {
    configurable: true,
    enumerable: true,
    writable: false,
    value: undefined,
    get: undefined,
    set: undefined
  }

  if (isDataDescriptor(obj) || hasOwn(obj, 'value')) {
    return extend({}, omit(options, ['get', 'set']), obj)
  }

  if (isAccessorDescriptor(obj) || hasOwn(obj, 'get')) {
    return extend({}, omit(options, ['writable', 'value']), obj)
  }
}

module.exports = defineProperty
<commit_msg>update lang/defineProp<commit_after>var map                  = require('../object/map')
var omit                 = require('../object/omit')
var extend               = require('../object/extend')
var hasOwn               = require('../object/hasOwn')
var isFunction           = require('./isFunction')
var isPlainObject        = require('./isPlainObject')
var isDataDescriptor     = require('./isDescriptorData')
var isAccessorDescriptor = require('./isDescriptorAccessor')

// This method allows precise addition to or modification of a property on an object.

// Normal property addition through assignment creates properties which show up during property enumeration (for...in loop or Object.keys method), whose values may be changed, and which may be deleted. This method allows these extra details to be changed from their defaults.

// By default, values added using Object.defineProperty() are immutable.

// Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A descriptor must be one of these two flavors; it cannot be both.
// Both data and accessor descriptors are objects.

// They share the following required keys: configurable, enumerable

// A data descriptor is a property that has a value, which may or may not be writable.

// An accessor descriptor is a property described by a getter-setter pair of functions.

// This is extrememly raw.
// obj: [object] object to be mutated
// prop: [String] property to add.
// config: [Boolean|Object] boolean for configurable key or descriptor object
// enumerable: [Boolean] boolean for enumerable key
// a: [Boolean|Function] boolean if data-descriptor (alias for writable key), function if accessor-descriptor (alias for getter function)
// b: [AnyValue|Function] a javascript value if data-descriptor (alias for value), function if accessor-descriptor (alias for setter function)

function defineProperty (obj, prop, config, enumerable, a, b) {
  // multiple definition
  if (isPlainObject(prop)) {
    return Object.defineProperties(obj, map(prop, definePropDefaults))
  }
  // implicit definition
  if (isPlainObject(config)) {
    return Object.defineProperty(obj, prop, definePropDefaults(config))
  }

  // explicit definition
  var configuration = {}
  configuration.configurable = !!config
  configuration.enumerable = !!enumerable

  if (isFunction(a)) {
    configuration.get = a
    configuration.set = b
  } else {
    configuration.writable = !!a
    configuration.value = b
  }

  return Object.defineProperty(obj, prop, configuration)
}

function definePropDefaults (obj) {
  var options = {
    configurable: true,
    enumerable: true,
    writable: false,
    value: undefined,
    get: undefined,
    set: undefined
  }

  if (isDataDescriptor(obj) || hasOwn(obj, 'value')) {
    return extend({}, omit(options, ['get', 'set']), obj)
  }

  if (isAccessorDescriptor(obj) || hasOwn(obj, 'get')) {
    return extend({}, omit(options, ['writable', 'value']), obj)
  }
}

module.exports = defineProperty
<|endoftext|>