<commit_before>const emitter = require('emitter-io');

module.exports = function (RED) {
    let status;
    let client;

    function CenConfigNode(config) {
        RED.nodes.createNode(this, config);

        let self = this;

        self.users = {};

        self.host = config.host;
        self.user = config.user;
        self.channel = config.channel || 'public';
        self.secret = self.credentials.secret;
        self.info = "";

        let client = emitter.connect({
            host: self.host,
            port: 8800,
            keepalive: 30,
            secure: false
        });
        client.on('message', function(msg){
            var msg = message;
            Object.keys(self.users).forEach(function (id) {
                self.users[id].emit("input", msg.asString());
            });
        });
        client.subscribe(self.secret, self.channel);

        this.register = function (clientNode) {
            self.users[clientNode.id] = clientNode;
        };

        this.deregister = function (clientNode, done) {
            delete self.users[clientNode.id];
            return done();
        };

        this.setStatus = function (c) {
            status = c;
            let s;
            switch (c) {
                case 'connecting':
                    s = {
                        fill: 'yellow',
                        shape: 'ring',
                        text: 'node-red:common.status.connecting'
                    };
                    break;
                case 'prompt':
                    s = {
                        fill: 'yellow',
                        shape: 'ring',
                        text: c
                    };
                    break;
                case 'connect':
                    s = {
                        fill: 'green',
                        shape: 'dot',
                        text: 'node-red:common.status.connected'
                    };
                    break;
                case 'disconnected':
                    s = {
                        fill: 'red',
                        shape: 'ring',
                        text: 'node-red:common.status.disconnected'
                    };
                    break;
                default:
                    s = {
                        fill: 'red',
                        shape: 'ring',
                        text: c
                    };
            }

            Object.keys(self.users).forEach(function (id) {
                self.users[id].status(s);
            });
        };
    }

    // !Register
    RED.nodes.registerType('em-config', CenConfigNode, {
        credentials: {
            secret: {
                type: 'text'
            }
        }
    });
};
<commit_msg>Yet another misleading js doc<commit_after>const emitter = require('emitter-io');

module.exports = function (RED) {
    let status;
    let client;

    function CenConfigNode(config) {
        RED.nodes.createNode(this, config);

        let self = this;

        self.users = {};

        self.host = config.host;
        self.user = config.user;
        self.channel = config.channel || 'public/';
        self.secret = self.credentials.secret;
        self.info = "";

        let client = emitter.connect({
            host: self.host,
            port: 8800,
            keepalive: 30,
            secure: false
        });
        client.on('message', function(msg){
            var msg = message;
            Object.keys(self.users).forEach(function (id) {
                self.users[id].emit("input", msg.asString());
            });
        });
        client.subscribe({
            key : self.secret,
            channel : self.channel
        });

        this.register = function (clientNode) {
            self.users[clientNode.id] = clientNode;
        };

        this.deregister = function (clientNode, done) {
            delete self.users[clientNode.id];
            return done();
        };

        this.setStatus = function (c) {
            status = c;
            let s;
            switch (c) {
                case 'connecting':
                    s = {
                        fill: 'yellow',
                        shape: 'ring',
                        text: 'node-red:common.status.connecting'
                    };
                    break;
                case 'prompt':
                    s = {
                        fill: 'yellow',
                        shape: 'ring',
                        text: c
                    };
                    break;
                case 'connect':
                    s = {
                        fill: 'green',
                        shape: 'dot',
                        text: 'node-red:common.status.connected'
                    };
                    break;
                case 'disconnected':
                    s = {
                        fill: 'red',
                        shape: 'ring',
                        text: 'node-red:common.status.disconnected'
                    };
                    break;
                default:
                    s = {
                        fill: 'red',
                        shape: 'ring',
                        text: c
                    };
            }

            Object.keys(self.users).forEach(function (id) {
                self.users[id].status(s);
            });
        };
    }

    // !Register
    RED.nodes.registerType('em-config', CenConfigNode, {
        credentials: {
            secret: {
                type: 'text'
            }
        }
    });
};
<|endoftext|>


<commit_before>/**
 * Created by anyuzer on 2014-06-16.
 */

(function(KUBE){
    "use strict";
    KUBE.LoadFactory('Theme', Theme,['DomJack','StyleJack','Hash','ExtendObject','Color']);
    Theme.prototype.toString = function(){ return '[object '+this.constructor.name+']' };

    function Theme(_name){
        var $ThemeAPI,theme,quickSets,SJ;
        quickSets = {};
        SJ = KUBE.StyleJack;

        $ThemeAPI = {
            'Font':Font(),
            'Background':Background(),
            'Border':Border(),
            'Padding':Padding(),
            'SetUnitSize':SetUnitSize,
            'GetUnitSize':GetUnitSize,
            'GetAppearanceList':GetAppearanceList,
            'GetName':GetName,
            'AddQuickSet':AddQuickSet,
            'GetQuickSet':GetQuickSet
        }.KUBE().create(Theme.prototype);
        initTheme();
        return $ThemeAPI;

        function GetName(){
            return _name;
        }

        function GetAppearanceList(){
            return theme.properties;
        }

        function GetQuickSet(_name){
            var $return = {};
            if(KUBE.Is(_name) === 'string' && quickSets[_name]){
                $return = quickSets[_name].GetUsing();
            }
            return $return;
        }

        //Font
        function Font(){
            return {
                'AddColor':AddColor,
                'AddSize':AddSize,
                'AddFamily':AddFamily,
                'AddStyle':AddStyle,
                'SetStyle':SetStyle
            };

            function AddColor(_name,_color){
                if(validateColor(_color) && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].color = _color;
                }
            }

            function AddSize(_name,_size){
                if(KUBE.Is(_size) === 'number' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].size = _size;
                }
            }

            function AddFamily(_name,_family){
                if(KUBE.Is(_family) === 'string' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].family = _family;
                }
            }

            function AddStyle(_name,_style){
                if(KUBE.Is(_style) === 'string' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].style = _style;
                }
            }

            function AddWeight(_name,_weight){
                if(KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].weight = _weight;
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.font[_name];

                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    definition.KUBE().each(function(_key,_val){
                        applyStyle(_StyleJack,_key,_val);
                    });
                }
            }

            function applyStyle(_SJ,_type,_value){
                switch(_type){
                    case 'color':
                        _SJ.Color(_value);
                        break;

                    case 'size':
                        _SJ.Font().Size(calcSize(_value));
                        break;

                    case 'family':
                        _SJ.Font().Family(_value);
                        break;

                    case 'Weight':
                        _SJ.Font().Weight(_value);
                        break;

                    case 'Style':
                        _SJ.Font().Style(_value);
                        break;
                }
            }

            function initSet(_name){
                if(!theme.properties.font[_name]){
                    theme.properties.font[_name] = {};
                }
            }
        }

        //Background
        function Background(){
            return {
                'AddColor':AddColor,
                'SetStyle':SetStyle
            };

            function AddColor(_name,_color){
                if(validateColor(_color) && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.background[_name].color = _color;
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.background[_name];
                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    definition.KUBE().each(function(_key,_val){
                        applyStyle(_StyleJack,_key,_val);
                    });
                }
            }

            function applyStyle(_SJ,_type,_value){
                switch(_type){
                    case 'color':
                        _SJ.Background().Color(_value);
                        break;
                }
            }

            function initSet(_name){
                if(!theme.properties.background[_name]){
                    theme.properties.background[_name] = {};
                }
            }
        }

        //Border
        function Border(){
            return {
                'AddColor':AddColor,
                'AddWidth':AddWidth,
                'AddRadius':AddRadius,
                'SetStyle':SetStyle
            };

            function AddColor(_name,_color){
                if(validateColor(_color) && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.border[_name].color = _color;
                }
            }

            function AddWidth(_name,_pxSize){
                if(KUBE.Is(_pxSize) === 'number' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.border[_name].width = _pxSize;
                }
            }

            function AddRadius(_name,_c1,_c2,_c3,_c4){
                if(KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    _c1 = (KUBE.Is(_c1) === 'number' ? _c1 : 0);
                    _c2 = (KUBE.Is(_c2) === 'number' ? _c2 : 0);
                    _c3 = (KUBE.Is(_c3) === 'number' ? _c3 : 0);
                    _c4 = (KUBE.Is(_c4) === 'number' ? _c4 : 0);
                    theme.properties.border[_name].radius = [_c1,_c2,_c3,_c4];
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.border[_name];
                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    definition.KUBE().each(function(_key,_val){
                        applyStyle(_StyleJack,_key,_val);
                    });
                }
            }

            function applyStyle(_SJ,_type,_value){
                switch(_type){
                    case 'color':
                        //Obviously this isn't right, just for the short term
                        _SJ.Border().Width(1);
                        _SJ.Border().Style('solid');
                        _SJ.Border().Color(_value);
                        break;
                }
            }

            function initSet(_name){
                if(!theme.properties.border[_name]){
                    theme.properties.border[_name] = {};
                }
            }
        }

        //Padding
        function Padding(){
            return {
                'AddTop':AddTop,
                'AddRight':AddRight,
                'AddBottom':AddBottom,
                'AddLeft':AddLeft,
                'AddAll':AddAll,
                'SetStyle':SetStyle
            };

            function AddTop(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].top = calcSize(_value);
                }
            }

            function AddRight(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].right = calcSize(_value);
                }
            }

            function AddBottom(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].bottom = calcSize(_value);
                }
            }

            function AddLeft(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].left = calcSize(_value);
                }
            }

            function AddAll(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].top = calcSize(_value);
                    theme.properties.padding[_name].right = calcSize(_value);
                    theme.properties.padding[_name].bottom = calcSize(_value);
                    theme.properties.padding[_name].left = calcSize(_value);
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.padding[_name];
                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    _StyleJack.Padding(theme.properties.padding[_name]);
                }
            }

            function initSet(_name){
                if(!theme.properties.padding[_name]){
                    theme.properties.padding[_name] = {'top':0,'right':0,'bottom':0,'left':0};
                }
            }
        }

        function AddQuickSet(_name,_Appearance){
            if(KUBE.Is(_Appearance,true) === 'Appearance' && KUBE.Is(_name) === 'string'){
                quickSets[_name] = _Appearance;
            }
        }

        //Unit size is used to calculate all theme measurements except for border width (because that seems crazy)
        function SetUnitSize(_unitSize){
            if(KUBE.Is(_unitSize) === 'number'){
                theme.unitSize = _unitSize;
            }
        }

        function GetUnitSize(){
            return theme.unitSize;
        }

        //Private
        function initTheme(){
            theme = {};
            theme.unitSize = 3;
            theme.properties = {
                'border':{},
                'background':{},
                'font':{}
            };
        }

        function validateColor(_color){
            var Color = KUBE.Color();
            return Color.IsValidColor(_color);
        }

        function calcSize(_size,_round){
            _round = (KUBE.Is(_round) === 'boolean' ? _round : true);
            return (_round ? Math.round(_size*theme.unitSize) : _size*theme.unitSize);
        }
    }
})(KUBE);<commit_msg>stuff<commit_after>/**
 * Created by anyuzer on 2014-06-16.
 */

(function(KUBE){
    "use strict";
    KUBE.LoadFactory('Theme', Theme,['DomJack','StyleJack','Hash','ExtendObject','Color']);
    Theme.prototype.toString = function(){ return '[object '+this.constructor.name+']' };

    function Theme(_name){
        var $ThemeAPI,theme,quickSets,SJ;
        quickSets = {};
        SJ = KUBE.StyleJack;

        $ThemeAPI = {
            'Font':Font(),
            'Background':Background(),
            'Border':Border(),
            'Padding':Padding(),
            'SetUnitSize':SetUnitSize,
            'GetUnitSize':GetUnitSize,
            'GetAppearanceList':GetAppearanceList,
            'GetName':GetName,
            'AddQuickSet':AddQuickSet,
            'GetQuickSet':GetQuickSet
        }.KUBE().create(Theme.prototype);
        initTheme();
        return $ThemeAPI;

        function GetName(){
            return _name;
        }

        function GetAppearanceList(){
            return theme.properties;
        }

        function GetQuickSet(_name){
            var $return = {};
            if(KUBE.Is(_name) === 'string' && quickSets[_name]){
                $return = quickSets[_name].GetUsing();
            }
            return $return;
        }

        //Font
        function Font(){
            return {
                'AddColor':AddColor,
                'AddSize':AddSize,
                'AddFamily':AddFamily,
                'AddStyle':AddStyle,
                'SetStyle':SetStyle
            };

            function AddColor(_name,_color){
                if(validateColor(_color) && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].color = _color;
                }
            }

            function AddSize(_name,_size){
                if(KUBE.Is(_size) === 'number' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].size = _size;
                }
            }

            function AddFamily(_name,_family){
                if(KUBE.Is(_family) === 'string' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].family.push(_family);
                }
            }

            function AddStyle(_name,_style){
                if(KUBE.Is(_style) === 'string' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].style = _style;
                }
            }

            function AddWeight(_name,_weight){
                if(KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.font[_name].weight = _weight;
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.font[_name];

                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    definition.KUBE().each(function(_key,_val){
                        applyStyle(_StyleJack,_key,_val);
                    });
                }
            }

            function applyStyle(_SJ,_type,_value){
                switch(_type.toLowerCase()){
                    case 'color':
                        _SJ.Color(_value);
                        break;

                    case 'size':
                        _SJ.Font().Size(calcSize(_value));
                        break;

                    case 'family':
                        _SJ.Font().Family(_value);
                        break;

                    case 'weight':
                        _SJ.Font().Weight(_value);
                        break;

                    case 'style':
                        _SJ.Font().Style(_value);
                        break;
                }
            }

            function initSet(_name){
                if(!theme.properties.font[_name]){
                    theme.properties.font[_name] = {};
                    theme.properties.font[_name].family = [];
                }
            }
        }

        //Background
        function Background(){
            return {
                'AddColor':AddColor,
                'SetStyle':SetStyle
            };

            function AddColor(_name,_color){
                if(validateColor(_color) && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.background[_name].color = _color;
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.background[_name];
                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    definition.KUBE().each(function(_key,_val){
                        applyStyle(_StyleJack,_key,_val);
                    });
                }
            }

            function applyStyle(_SJ,_type,_value){
                switch(_type){
                    case 'color':
                        _SJ.Background().Color(_value);
                        break;
                }
            }

            function initSet(_name){
                if(!theme.properties.background[_name]){
                    theme.properties.background[_name] = {};
                }
            }
        }

        //Border
        function Border(){
            //TODO: BorderStyle (Inset/etc)
            return {
                'AddColor':AddColor,
                'AddWidth':AddWidth,
                'AddRadius':AddRadius,
                'SetStyle':SetStyle
            };

            function AddColor(_name,_color){
                if(validateColor(_color) && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.border[_name].color = _color;
                }
            }

            function AddWidth(_name,_pxSize){
                if(KUBE.Is(_pxSize) === 'number' && KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    theme.properties.border[_name].width = _pxSize;
                }
            }

            function AddRadius(_name,_c1,_c2,_c3,_c4){
                if(KUBE.Is(_name) === 'string'){
                    initSet(_name);
                    _c1 = (KUBE.Is(_c1) === 'number' ? _c1 : 0);
                    _c2 = (KUBE.Is(_c2) === 'number' ? _c2 : 0);
                    _c3 = (KUBE.Is(_c3) === 'number' ? _c3 : 0);
                    _c4 = (KUBE.Is(_c4) === 'number' ? _c4 : 0);
                    theme.properties.border[_name].radius = [_c1,_c2,_c3,_c4];
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.border[_name];
                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    definition.KUBE().each(function(_key,_val){
                        applyStyle(_StyleJack,_key,_val);
                    });
                }
            }

            function applyStyle(_SJ,_type,_value){
                switch(_type){
                    case 'color':
                        //Obviously this isn't right, just for the short term
                        _SJ.Border().Width(1);
                        _SJ.Border().Style('solid');
                        _SJ.Border().Color(_value);
                        break;
                }
            }

            function initSet(_name){
                if(!theme.properties.border[_name]){
                    theme.properties.border[_name] = {};
                }
            }
        }

        //Padding
        function Padding(){
            return {
                'AddTop':AddTop,
                'AddRight':AddRight,
                'AddBottom':AddBottom,
                'AddLeft':AddLeft,
                'AddAll':AddAll,
                'SetStyle':SetStyle
            };

            function AddTop(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].top = calcSize(_value);
                }
            }

            function AddRight(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].right = calcSize(_value);
                }
            }

            function AddBottom(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].bottom = calcSize(_value);
                }
            }

            function AddLeft(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].left = calcSize(_value);
                }
            }

            function AddAll(_name,_value){
                if(KUBE.Is(_name) === 'string' && KUBE.Is(_value) === 'number'){
                    initSet(_name);
                    theme.properties.padding[_name].top = calcSize(_value);
                    theme.properties.padding[_name].right = calcSize(_value);
                    theme.properties.padding[_name].bottom = calcSize(_value);
                    theme.properties.padding[_name].left = calcSize(_value);
                }
            }

            function SetStyle(_name,_StyleJack){
                var definition = theme.properties.padding[_name];
                if(KUBE.Is(_StyleJack,true) === 'StyleJack' && KUBE.Is(definition) === 'object'){
                    _StyleJack.Padding(theme.properties.padding[_name]);
                }
            }

            function initSet(_name){
                if(!theme.properties.padding[_name]){
                    theme.properties.padding[_name] = {'top':0,'right':0,'bottom':0,'left':0};
                }
            }
        }

        function AddQuickSet(_name,_Appearance){
            if(KUBE.Is(_Appearance,true) === 'Appearance' && KUBE.Is(_name) === 'string'){
                quickSets[_name] = _Appearance;
            }
        }

        //Unit size is used to calculate all theme measurements except for border width (because that seems crazy)
        function SetUnitSize(_unitSize){
            if(KUBE.Is(_unitSize) === 'number'){
                theme.unitSize = _unitSize;
            }
        }

        function GetUnitSize(){
            return theme.unitSize;
        }

        //Private
        function initTheme(){
            theme = {};
            theme.unitSize = 3;
            theme.properties = {
                'border':{},
                'background':{},
                'font':{}
            };
        }

        function validateColor(_color){
            var Color = KUBE.Color();
            return Color.IsValidColor(_color);
        }

        function calcSize(_size,_round){
            _round = (KUBE.Is(_round) === 'boolean' ? _round : true);
            return (_round ? Math.round(_size*theme.unitSize) : _size*theme.unitSize);
        }
    }
})(KUBE);<|endoftext|>


<commit_before>/* eslint-disable */
import {MP4Box} from 'mp4box'

var HIGH_WATER_MARK = 10000000; // 1MB
var LOW_WATER_MARK = 1000000; // 100kB
var APPEND_RETRY_TIME = 5; // seconds

/**
 * Stream data from `file` into `this.mediaElem`.
 * `file` must be an object with a `length` property giving the file size in bytes,
 * and a `createReadStream(opts)` method that retunr a string and accepts opts.start
 * and opts.end to specify a byte range (inclusive) to fetch.
 * @param {File} file described above
 * @param {HTMLthis.MediaElement} this.mediaElem <audio> or <video> element
 * @param {Object} opts Options
 * @param {number=} opts.this.debugTrack Track to save for debugging. Defaults to -1 (none)
 */
class VideStream {
	constructor(file, mediaElem, opts) {

		opts = opts || {};
		this.debugTrack = opts.debugTrack || -1;
		this.debugBuffers = [];
		this.mediaElem = mediaElem
		this.mediaElem.addEventListener('waiting',  () => {
			if (this.ready) {
				this.seek(this.mediaElem.currentTime);
			}
		});

		this.mediaSource = new MediaSource();
		this.mediaSource.addEventListener('sourceopen',  () =>  {
			makeRequest(0);
		});
		this.mediaElem.src = window.URL.createObjectURL(this.mediaSource);

		this.mp4box = new MP4Box();
		this.mp4box.onError =  this.handleMp4BoxError
		this.ready = false;
		this.totalWaitingBytes = 0;
		this.tracks = {}; // keyed by track id
		this.mp4box.onReady =  this.setupVideo

		this.mp4box.onSegment = this.handleSegment

		this.requestOffset; // Position in the file where `stream` will next provide data
		this.stream = null;
		// var stream
		this.detachStream = null;
		var makeRequest = (pos)  => {
			if (pos === file.length) {
				this.mp4box.flush(); // All done!
				return;
			}

			if (this.stream && pos === this.requestOffset) {
				return; // There is althis.ready a stream at the right position, so just let it continue
			}

			if (this.stream) {
				this.stream.destroy(); // There is a stream, but not at the right position
				this.detachStream();
			}

			this.requestOffset = pos;
			var opts = {
				start: this.requestOffset,
				end: file.length - 1
			};
			// There is necessarily only one stream that is not detached/destroyed at one time,
			// so it's safe to overwrite the var from the outer scope
			// stream = file.createReadStream(opts);
			this.stream = file.createReadStream(opts);
			var onData = (data) => {
				// Pause the stream and resume it on the next run of the event loop to avoid
				// lots of 'data' event blocking the UI
				this.stream.pause();
				// Only resume if there isn't too much data that this.mp4box has processed that hasn't
				// gone to the browser
				if (this.totalWaitingBytes <= HIGH_WATER_MARK) {
					this.resumeStream();
				}

				var arrayBuffer = data.toArrayBuffer(); // TODO: avoid copy
				// sample output here http://pastebin.com/dyC9ME3P
				console.group('Array Buffer Append')
				console.log('Appending array buffer', arrayBuffer, 'byteLength', arrayBuffer.byteLength)
				arrayBuffer.fileStart = this.requestOffset;
				this.requestOffset += arrayBuffer.byteLength;
				var nextOffset;
				// try {
					// MP4Box tends to blow up ungracefully when it can't parse the mp4 input, so
					// use a try/catch
					nextOffset = this.mp4box.appendBuffer(arrayBuffer);
					// // Prevent infinte loops if this.mp4box keeps requesting the same data
					// if (nextOffset === arrayBuffer.fileStart) {
					// 	throw new Error('MP4Box parsing stuck at offset: ' + nextOffset);
					// }
				// } catch (err) {
				// 	console.error('MP4Box threw exception:', err);
				// 	// This will fire the 'error' event on the audio/video element
				// 	if (this.mediaSource.readyState === 'open') {
				// 		this.mediaSource.endOfStream('decode');
				// 	}
				// 	stream.destroy();
				// 	this.detachStream();
				// 	throw err
				// }
				console.log('Next Offset', nextOffset)
				console.groupEnd('Array Buffer Append')
				makeRequest(nextOffset);
			}
			this.stream.on('data', onData);
			var onEnd = ()  => {
				this.detachStream();
				makeRequest(this.requestOffset);
			}
			this.stream.on('end', onEnd);
			var onStreamError  = (err) => {
				console.error('Stream error:', err);
				if (this.mediaSource.readyState === 'open') {
					this.mediaSource.endOfStream('network');
				}
			}
			this.stream.on('error', onStreamError);

			this.detachStream =  () => {
				this.stream.removeListener('data', onData);
				this.stream.removeListener('end', onEnd);
				this.stream.removeListener('error', onStreamError);
				// stream = null
				this.stream = null
				this.detachStream = null;
			}
		}

		this.resumeStream = () =>  {
			// Always wait till the next run of the event loop to cause async break
			setTimeout( () => {
				if (this.stream) {
					// TODO: remove stream._readableState.flowing once stream.isPaused is available
					if (this.stream.isPaused ? this.stream.isPaused() : !this.stream._readableState.flowing) {
						this.stream.resume();
					}
				}
			});
		}
	}
	handleSegment = (id, user, buffer, nextSample) => {
				var track = this.tracks[id];
				this.appendBuffer(track, buffer, nextSample === track.meta.nb_samples);
				if (id === this.debugTrack && this.debugBuffers) {
					this.debugBuffers.push(buffer);
					if (nextSample > 1000) {
						save('track-' + this.debugTrack + '.mp4', this.debugBuffers);
						this.debugBuffers = null;
					}
				}
			};
	handleMp4BoxError = (e) => {
			console.error('MP4Box error:', e);
			if(this.detachStream) {
				this.detachStream();
			}
			if (this.mediaSource.readyState === 'open') {
				this.mediaSource.endOfStream('decode');
			}
		};
	updateEnded = () => {
		if (this.mediaSource.readyState !== 'open') {
			return;
		}

		var ended = Object.keys(this.tracks).every((id) => {
			var track = this.tracks[id];
			return track.ended && !track.buffer.updating;
		});

		if (ended && this.mediaSource.readyState === 'open') {
			this.mediaSource.endOfStream();
		}
	}
	seek = (seconds) => {
		var seekResult = this.mp4box.seek(seconds, true);
		console.log('Seeking to time: ', seconds);
		console.log('Seeked file offset:', this.seekResult.offset);
		makeRequest(this.seekResult.offset);
		this.resumeStream();
	}
	appendBuffer = (track, buffer, ended) => {
		this.totalWaitingBytes += buffer.byteLength;
		track.arrayBuffers.push({
			buffer: buffer,
			ended: ended || false
		});
		this.popBuffers(track);
	}
	popBuffers = (track) => {
		if (track.buffer.updating || track.arrayBuffers.length === 0) return;
		var buffer = track.arrayBuffers.shift();
		var appended = false;
		try {
			track.buffer.appendBuffer(buffer.buffer);
			track.ended = buffer.ended;
			appended = true;
		} catch (e) {
			console.error('SourceBuffer error: ', e);
			// Wait and try again later (assuming buffer space was the issue)
			track.arrayBuffers.unshift(buffer);
			setTimeout(() => {
				this.popBuffers(track);
			}, APPEND_RETRY_TIME);
		}
		if (appended) {
			this.totalWaitingBytes -= buffer.buffer.byteLength;
			if (this.totalWaitingBytes <= LOW_WATER_MARK) {
				this.resumeStream();
			}
			this.updateEnded(); // call this.mediaSource.endOfStream() if needed
		}
	}
	setupVideo = (info) => {
			console.log('MP4 info:', info);
			info.tracks.forEach((track) => {
				var mime;
				if (track.video) {
					mime = 'video/mp4';
				} else if (track.audio) {
					mime = 'audio/mp4';
				} else {
					return;
				}
				mime += '; codecs="' + track.codec + '"';
				if (MediaSource.isTypeSupported(mime)) {
					var sourceBuffer = this.mediaSource.addSourceBuffer(mime);
					var trackEntry = {
						buffer: sourceBuffer,
						arrayBuffers: [],
						meta: track,
						ended: false
					};
					sourceBuffer.addEventListener('updateend', this.popBuffers.bind(null, trackEntry));
					this.mp4box.setSegmentOptions(track.id, null, {
						// It really isn't that inefficient to give the data to the browser on every frame (for video)
						nbSamples: track.video ? 1 : 100
					});
					this.tracks[track.id] = trackEntry
				}
			});

			var initSegs = this.mp4box.initializeSegmentation();
			initSegs.forEach((initSegment) => {
				this.appendBuffer(this.tracks[initSegment.id], initSegment.buffer);
				if (initSegment.id === this.debugTrack) {
					save('init-track-' + this.debugTrack + '.mp4', [initSegment.buffer]);
					this.debugBuffers.push(initSegment.buffer);
				}
			});
			this.ready = true;
		};
}

export default VideStream

/**
  Saves an array of ArrayBuffers to the given filename.
  @param {string} filename Filename to save as.
  @param {Array.<ArrayBuffer>}
  */
function save (filename, buffers) {
	var blob = new Blob(buffers);
	var url = URL.createObjectURL(blob);
	var a = document.createElement('a');
	a.setAttribute('href', url);
	a.setAttribute('download', filename);
	a.click();
 }
<commit_msg>converted to class<commit_after>/* eslint-disable */
import {MP4Box} from 'mp4box'

var HIGH_WATER_MARK = 10000000; // 1MB
var LOW_WATER_MARK = 1000000; // 100kB
var APPEND_RETRY_TIME = 5; // seconds

/**
 * Stream data from `file` into `this.mediaElem`.
 * `file` must be an object with a `length` property giving the file size in bytes,
 * and a `createReadStream(opts)` method that retunr a string and accepts opts.start
 * and opts.end to specify a byte range (inclusive) to fetch.
 * @param {File} file described above
 * @param {HTMLthis.MediaElement} this.mediaElem <audio> or <video> element
 * @param {Object} opts Options
 * @param {number=} opts.this.debugTrack Track to save for debugging. Defaults to -1 (none)
 */
class VideStream {
  constructor(file, mediaElem, opts) {
    opts = opts || {}
    this.debugTrack = opts.debugTrack || -1;
    this.debugBuffers = [];
    this.mediaElem = mediaElem
    this.file = file
    this.mediaElem.addEventListener('waiting', () => {
      if (this.ready) {
        this.seek(this.mediaElem.currentTime);
      }
    });
    this.mediaSource = new MediaSource();
    this.mediaSource.addEventListener('sourceopen', () => {
      this.makeRequest(0);
    });
    this.mediaElem.src = window.URL.createObjectURL(this.mediaSource);
    this.mp4box = new MP4Box();
    this.mp4box.onError = this.handleMp4BoxError
    this.ready = false;
    this.totalWaitingBytes = 0;
    this.tracks = {}; // keyed by track id
    this.mp4box.onReady = this.setupVideo
    this.mp4box.onSegment = this.handleSegment
    this.requestOffset; // Position in the file where `stream` will next provide data
    this.stream = null;
    // var stream
    this.detachStream = null;
  }
  resumeStream = () => {
    // Always wait till the next run of the event loop to cause async break
    setTimeout(() => {
      if (this.stream) {
        // TODO: remove stream._readableState.flowing once stream.isPaused is available
        if (this.stream.isPaused ? this.stream.isPaused() : !this.stream._readableState.flowing) {
          this.stream.resume();
        }
      }
    })
  }
  makeRequest = (pos) => {
    if (pos === this.file.length) {
      this.mp4box.flush(); // All done!
      return;
    }

    if (this.stream && pos === this.requestOffset) {
      return; // There is althis.ready a stream at the right position, so just let it continue
    }

    if (this.stream) {
      this.stream.destroy(); // There is a stream, but not at the right position
      this.detachStream();
    }

    this.requestOffset = pos;
    var opts = {
      start: this.requestOffset,
      end: this.file.length - 1
    };
    // There is necessarily only one stream that is not detached/destroyed at one time,
    // so it's safe to overwrite the var from the outer scope
    // stream = file.createReadStream(opts);
    this.stream = this.file.createReadStream(opts);
    var onData = (data) => {
      // Pause the stream and resume it on the next run of the event loop to avoid
      // lots of 'data' event blocking the UI
      this.stream.pause();
      // Only resume if there isn't too much data that this.mp4box has processed that hasn't
      // gone to the browser
      if (this.totalWaitingBytes <= HIGH_WATER_MARK) {
        this.resumeStream();
      }

      var arrayBuffer = data.toArrayBuffer(); // TODO: avoid copy
      // sample output here http://pastebin.com/dyC9ME3P
      console.group('Array Buffer Append')
      console.log('Appending array buffer', arrayBuffer, 'byteLength', arrayBuffer.byteLength)
      arrayBuffer.fileStart = this.requestOffset;
      this.requestOffset += arrayBuffer.byteLength;
      var nextOffset;
      // try {
      // MP4Box tends to blow up ungracefully when it can't parse the mp4 input, so
      // use a try/catch
      nextOffset = this.mp4box.appendBuffer(arrayBuffer);
      // // Prevent infinte loops if this.mp4box keeps requesting the same data
      // if (nextOffset === arrayBuffer.fileStart) {
      //  throw new Error('MP4Box parsing stuck at offset: ' + nextOffset);
      // }
      // } catch (err) {
      //  console.error('MP4Box threw exception:', err);
      //  // This will fire the 'error' event on the audio/video element
      //  if (this.mediaSource.readyState === 'open') {
      //      this.mediaSource.endOfStream('decode');
      //  }
      //  stream.destroy();
      //  this.detachStream();
      //  throw err
      // }
      console.log('Next Offset', nextOffset)
      console.groupEnd('Array Buffer Append')
      this.makeRequest(nextOffset);
    }
    this.stream.on('data', onData);
    var onEnd = () => {
      this.detachStream();
      this.makeRequest(this.requestOffset);
    }
    this.stream.on('end', onEnd);
    var onStreamError = (err) => {
      console.error('Stream error:', err);
      if (this.mediaSource.readyState === 'open') {
        this.mediaSource.endOfStream('network');
      }
    }
    this.stream.on('error', onStreamError);

    this.detachStream = () => {
      this.stream.removeListener('data', onData);
      this.stream.removeListener('end', onEnd);
      this.stream.removeListener('error', onStreamError);
      // stream = null
      this.stream = null
      this.detachStream = null;
    }
  }
  handleSegment = (id, user, buffer, nextSample) => {
    var track = this.tracks[id];
    this.appendBuffer(track, buffer, nextSample === track.meta.nb_samples);
    if (id === this.debugTrack && this.debugBuffers) {
      this.debugBuffers.push(buffer);
      if (nextSample > 1000) {
        save('track-' + this.debugTrack + '.mp4', this.debugBuffers);
        this.debugBuffers = null;
      }
    }
  };
  handleMp4BoxError = (e) => {
    console.error('MP4Box error:', e);
    if (this.detachStream) {
      this.detachStream();
    }
    if (this.mediaSource.readyState === 'open') {
      this.mediaSource.endOfStream('decode');
    }
  };
  updateEnded = () => {
    if (this.mediaSource.readyState !== 'open') {
      return;
    }

    var ended = Object.keys(this.tracks).every((id) => {
      var track = this.tracks[id];
      return track.ended && !track.buffer.updating;
    });

    if (ended && this.mediaSource.readyState === 'open') {
      this.mediaSource.endOfStream();
    }
  }
  seek = (seconds) => {
    var seekResult = this.mp4box.seek(seconds, true);
    console.log('Seeking to time: ', seconds);
    console.log('Seeked file offset:', this.seekResult.offset);
    this.makeRequest(this.seekResult.offset);
    this.resumeStream();
  }
  appendBuffer = (track, buffer, ended) => {
    this.totalWaitingBytes += buffer.byteLength;
    track.arrayBuffers.push({
      buffer: buffer,
      ended: ended || false
    });
    this.popBuffers(track);
  }
  popBuffers = (track) => {
    if (track.buffer.updating || track.arrayBuffers.length === 0) return;
    var buffer = track.arrayBuffers.shift();
    var appended = false;
    try {
      track.buffer.appendBuffer(buffer.buffer);
      track.ended = buffer.ended;
      appended = true;
    } catch (e) {
      console.error('SourceBuffer error: ', e);
      // Wait and try again later (assuming buffer space was the issue)
      track.arrayBuffers.unshift(buffer);
      setTimeout(() => {
        this.popBuffers(track);
      }, APPEND_RETRY_TIME);
    }
    if (appended) {
      this.totalWaitingBytes -= buffer.buffer.byteLength;
      if (this.totalWaitingBytes <= LOW_WATER_MARK) {
        this.resumeStream();
      }
      this.updateEnded(); // call this.mediaSource.endOfStream() if needed
    }
  }
  setupVideo = (info) => {
    console.log('MP4 info:', info);
    info.tracks.forEach((track) => {
      var mime;
      if (track.video) {
        mime = 'video/mp4';
      } else if (track.audio) {
        mime = 'audio/mp4';
      } else {
        return;
      }
      mime += '; codecs="' + track.codec + '"';
      if (MediaSource.isTypeSupported(mime)) {
        var sourceBuffer = this.mediaSource.addSourceBuffer(mime);
        var trackEntry = {
          buffer: sourceBuffer,
          arrayBuffers: [],
          meta: track,
          ended: false
        };
        sourceBuffer.addEventListener('updateend', this.popBuffers.bind(null, trackEntry));
        this.mp4box.setSegmentOptions(track.id, null, {
          // It really isn't that inefficient to give the data to the browser on every frame (for video)
          nbSamples: track.video ? 1 : 100
        });
        this.tracks[track.id] = trackEntry
      }
    });

    var initSegs = this.mp4box.initializeSegmentation();
    initSegs.forEach((initSegment) => {
      this.appendBuffer(this.tracks[initSegment.id], initSegment.buffer);
      if (initSegment.id === this.debugTrack) {
        save('init-track-' + this.debugTrack + '.mp4', [initSegment.buffer]);
        this.debugBuffers.push(initSegment.buffer);
      }
    });
    this.ready = true;
  };
}

export
default VideStream

/**
  Saves an array of ArrayBuffers to the given filename.
  @param {string} filename Filename to save as.
  @param {Array.<ArrayBuffer>}
  */
function save(filename, buffers) {
  var blob = new Blob(buffers);
  var url = URL.createObjectURL(blob);
  var a = document.createElement('a');
  a.setAttribute('href', url);
  a.setAttribute('download', filename);
  a.click();
}
<|endoftext|>


<commit_before>import React from "react-native";
import DiscussionItemContainer from "../containers/DiscussionItemContainer";
import PageEmpty from "./PageEmpty";
import PageLoading from "./PageLoading";
import LoadingItem from "./LoadingItem";
import StartDiscussionButton from "./StartDiscussionButton";
import BannerOfflineContainer from "../containers/BannerOfflineContainer";

const {
	StyleSheet,
	ListView,
	View
} = React;

const styles = StyleSheet.create({
	container: {
		paddingTop: 4,
		paddingBottom: 88
	},
	item: {
		overflow: "hidden"
	}
});

export default class Discussions extends React.Component {
	constructor(props) {
		super(props);

		this._dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
	}

	_getDataSource = () => {
		return this._dataSource.cloneWithRows(this.props.data);
	};

	render() {
		return (
			<View {...this.props}>
				<BannerOfflineContainer />

				{(() => {
					if (this.props.data.length === 0) {
						return <PageEmpty label="No discussions yet" image="sad" />;
					}

					if (this.props.data.length === 1) {
						switch (this.props.data[0]) {
						case "missing":
							return <PageLoading />;
						case "banned":
							return <PageEmpty label="You're banned in this community" image="meh" />;
						case "nonexistent":
							return <PageEmpty label="This community doesn't exist" image="sad" />;
						case "failed":
							return <PageEmpty label="Failed to load discussions" image="sad" />;
						}
					}

					return (
						<ListView
							removeClippedSubviews
							contentContainerStyle={styles.container}
							initialListSize={1}
							onEndReachedThreshold={1000}
							onEndReached={this.props.onEndReached}
							dataSource={this._getDataSource()}
							renderRow={thread => {
								if (thread === "missing") {
									return <LoadingItem />;
								}

								return (
									<DiscussionItemContainer
										key={thread.id}
										thread={thread}
										onNavigation={this.props.onNavigation}
										style={styles.item}
									/>
								);
							}}
						/>
					);
				})()}

				<StartDiscussionButton
					room={this.props.room}
					user={this.props.user}
					onNavigation={this.props.onNavigation}
				/>
			</View>
		);
	}
}

Discussions.propTypes = {
	data: React.PropTypes.arrayOf(React.PropTypes.oneOfType([
		React.PropTypes.oneOf([ "missing", "failed" ]),
		React.PropTypes.shape({
			id: React.PropTypes.string
		})
	])).isRequired,
	room: React.PropTypes.string.isRequired,
	user: React.PropTypes.string.isRequired,
	refreshData: React.PropTypes.func,
	onEndReached: React.PropTypes.func.isRequired,
	onNavigation: React.PropTypes.func.isRequired
};
<commit_msg>Refactor Discussions component<commit_after>import React from "react-native";
import DiscussionItemContainer from "../containers/DiscussionItemContainer";
import PageEmpty from "./PageEmpty";
import PageLoading from "./PageLoading";
import LoadingItem from "./LoadingItem";
import StartDiscussionButton from "./StartDiscussionButton";
import BannerOfflineContainer from "../containers/BannerOfflineContainer";

const {
	StyleSheet,
	ListView,
	View
} = React;

const styles = StyleSheet.create({
	container: {
		paddingTop: 4,
		paddingBottom: 88
	},
	item: {
		overflow: "hidden"
	}
});

export default class Discussions extends React.Component {
	constructor(props) {
		super(props);

		this._dataSource = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
	}

	_getDataSource = () => {
		return this._dataSource.cloneWithRows(this.props.data);
	};

	_renderRow = thread => {
		if (thread === "missing") {
			return <LoadingItem />;
		}

		return (
			<DiscussionItemContainer
				key={thread.id}
				thread={thread}
				onNavigation={this.props.onNavigation}
				style={styles.item}
			/>
		);
	};

	render() {
		let placeHolder;

		if (this.props.data.length === 0) {
			placeHolder = <PageEmpty label="No discussions yet" image="sad" />;
		} else if (this.props.data.length === 1) {
			switch (this.props.data[0]) {
			case "missing":
				placeHolder = <PageLoading />;
				break;
			case "banned":
				placeHolder = <PageEmpty label="You're banned in this community" image="meh" />;
				break;
			case "nonexistent":
				placeHolder = <PageEmpty label="This community doesn't exist" image="sad" />;
				break;
			case "failed":
				placeHolder = <PageEmpty label="Failed to load discussions" image="sad" />;
				break;
			}
		}

		return (
			<View {...this.props}>
				<BannerOfflineContainer />

				{placeHolder ? placeHolder :
					<ListView
						removeClippedSubviews
						contentContainerStyle={styles.container}
						initialListSize={1}
						onEndReachedThreshold={1000}
						onEndReached={this.props.onEndReached}
						dataSource={this._getDataSource()}
						renderRow={this._renderRow}
					/>
				}

				<StartDiscussionButton
					room={this.props.room}
					user={this.props.user}
					onNavigation={this.props.onNavigation}
				/>
			</View>
		);
	}
}

Discussions.propTypes = {
	data: React.PropTypes.arrayOf(React.PropTypes.oneOfType([
		React.PropTypes.oneOf([ "missing", "failed" ]),
		React.PropTypes.shape({
			id: React.PropTypes.string
		})
	])).isRequired,
	room: React.PropTypes.string.isRequired,
	user: React.PropTypes.string.isRequired,
	refreshData: React.PropTypes.func,
	onEndReached: React.PropTypes.func.isRequired,
	onNavigation: React.PropTypes.func.isRequired
};
<|endoftext|>


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

(function() {
  'use strict';

  angular.module('mesos', ['ngRoute', 'mesos.services', 'ui.bootstrap', 'ui.bootstrap.dialog']).
    config(['paginationConfig', '$routeProvider', function(paginationConfig, $routeProvider) {
      $routeProvider
        .when('/',
          {templateUrl: 'static/home.html', controller: 'HomeCtrl'})
        .when('/agents',
          {templateUrl: 'static/agents.html', controller: 'AgentsCtrl'})
        .when('/agents/:agent_id',
          {templateUrl: 'static/agent.html', controller: 'AgentCtrl'})
        .when('/agents/:agent_id/frameworks/:framework_id',
          {templateUrl: 'static/agent_framework.html', controller: 'AgentFrameworkCtrl'})
        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id',
          {templateUrl: 'static/agent_executor.html', controller: 'AgentExecutorCtrl'})
        .when('/frameworks',
          {templateUrl: 'static/frameworks.html', controller: 'FrameworksCtrl'})
        .when('/frameworks/:id',
          {templateUrl: 'static/framework.html', controller: 'FrameworkCtrl'})
        .when('/maintenance',
          {templateUrl: 'static/maintenance.html', controller: 'MaintenanceCtrl'})
        .when('/offers',
          {templateUrl: 'static/offers.html', controller: 'OffersCtrl'})
        .when('/roles',
          {templateUrl: 'static/roles.html', controller: 'RolesCtrl'})

        // TODO(tomxing): Remove the following '/slaves/*' paths once the
        // slave->agent rename is complete(MESOS-3779).
        .when('/slaves', {redirectTo: '/agents'})
        .when('/slaves/:agent_id', {redirectTo: '/agents/:agent_id'})
        .when('/slaves/:agent_id/frameworks/:framework_id',
          {redirectTo: '/agents/:agent_id/frameworks/:framework_id'})
        .when('/slaves/:agent_id/frameworks/:framework_id/executors/:executor_id',
          {redirectTo: '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id'})

        // Use a non-falsy template so the controller will still be executed.
        // Since the controller is intended only to redirect, the blank template
        // is fine.
        //
        // By design, controllers currently will not handle routes if the
        // template is falsy. There is an issue open in Angular to add that
        // feature:
        //
        //     https://github.com/angular/angular.js/issues/1838
        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse',
          {template: ' ', controller: 'AgentTaskAndExecutorRerouterCtrl'})
        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/tasks/:task_id/browse',
          {template: ' ', controller: 'AgentTaskAndExecutorRerouterCtrl'})
        .when('/agents/:agent_id/browse',
          {templateUrl: 'static/browse.html', controller: 'BrowseCtrl'})

        // TODO(tomxing): Remove the following '/slaves/*' paths once the
        // slave->agent rename is complete(MESOS-3779).
        .when('/slaves/:agent_id/frameworks/:framework_id/executors/:executor_id/browse',
          {redirectTo: '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse'})
        .when('/slaves/:agent_id/browse',
          {redirectTo: '/agents/:agent_id/browse'})
        .otherwise({redirectTo: '/'});

      // Configure [Angular UI Pagination][1]:
      //   * Show first/last buttons
      //   * Show 50 items per page
      //   * Show "..." when there are pages beyond the max shown
      //
      // [1] http://angular-ui.github.io/bootstrap/#/pagination
      paginationConfig.boundaryLinks = true;
      paginationConfig.rotate = false;
    }])
    .filter('truncateMesosID', function() {
      // Returns a truncated ID, for example:
      // Input: 9d4b2f2b-a759-4458-bebf-7d3507a6f0ca-S9
      // Output: ...bebf-7d3507a6f0ca-S9
      //
      // Note that an ellipsis is used for display purposes.
      return function(id) {
        if (id) {
          var truncatedIdParts = id.split('-');

          if (truncatedIdParts.length > 3) {
            return '\u2026' + truncatedIdParts.splice(3, 3).join('-');
          } else {
            return id;
          }
        } else {
          return '';
        }
      };
    })
    .filter('truncateMesosState', function() {
      return function(state) {
        // Remove the "TASK_" prefix.
        return state.substring(5);
      };
    })
    .filter('isoDate', function($filter) {
      return function(date) {
        var i = parseInt(date, 10);
        if (_.isNaN(i)) { return '' }
        return $filter('date')(i, 'yyyy-MM-ddTHH:mm:ssZ');
      };
    })
    .filter('relativeDate', function() {
      return function(date, refDate) {
        var i = parseInt(date, 10);
        if (_.isNaN(i)) { return '' }
        return relativeDate(i, refDate);
      };
    })
    .filter('slice', function() {
      return function(array, begin, end) {
        if (_.isArray(array)) {
          return array.slice(begin, end);
        }
      };
    })
    .filter('unixDate', function($filter) {
      return function(date) {
        if ((new Date(date)).getFullYear() == (new Date()).getFullYear()) {
          return $filter('date')(date, 'MMM dd HH:mm');
        } else {
          return $filter('date')(date, 'MMM dd yyyy');
        }
      };
    })
    // A filter that uses to convert small float number to decimal string.
    .filter('decimalFloat', function() {
      return function(num) {
        return num ? parseFloat(num.toFixed(4)).toString() : num;
      }
    })
    .filter('dataSize', function() {
      var BYTES_PER_KB = Math.pow(2, 10);
      var BYTES_PER_MB = Math.pow(2, 20);
      var BYTES_PER_GB = Math.pow(2, 30);
      var BYTES_PER_TB = Math.pow(2, 40);
      var BYTES_PER_PB = Math.pow(2, 50);
      // NOTE: Number.MAX_SAFE_INTEGER is 2^53 - 1

      return function(bytes) {
        if (bytes == null || isNaN(bytes)) {
          return '';
        } else if (bytes < BYTES_PER_KB) {
          return bytes.toFixed() + ' B';
        } else if (bytes < BYTES_PER_MB) {
          return (bytes / BYTES_PER_KB).toFixed() + ' KB';
        } else if (bytes < BYTES_PER_GB) {
          return (bytes / BYTES_PER_MB).toFixed() + ' MB';
        } else if (bytes < BYTES_PER_TB) {
          return (bytes / BYTES_PER_GB).toFixed(1) + ' GB';
        } else if (bytes < BYTES_PER_PB) {
          return (bytes / BYTES_PER_TB).toFixed(1) + ' TB';
        } else {
          return (bytes / BYTES_PER_PB).toFixed(1) + ' PB';
        }
      };
    })
    .directive('clipboard', [function() {
      return {
        restrict: 'A',
        scope: true,
        template: '',

        link: function(scope, element, attrs) {
          var clip = new Clipboard(element[0]);

          element.on('mouseenter', function() {
            element.addClass('clipboard-is-hover');
            element.triggerHandler('clipboardhover');
          });

          element.on('mouseleave', function() {
            // Restore tooltip content to its original value if it was
            // changed by this Clipboard instance.
            if (scope && scope.tt_content_orig) {
              scope.tt_content = scope.tt_content_orig;
              delete scope.tt_content_orig;
            }

            element.removeClass('clipboard-is-hover');
            element.triggerHandler('clipboardhover');
          });

          // Success for browsers with `execCommand` support.
          clip.on('success', function () {
            // Store the tooltip's original content so it can
            // be restored when the tooltip is hidden.
            scope.tt_content_orig = scope.tt_content;

            // Angular UI's Tooltip sets content on the element's scope in a
            // variable named 'tt_content'. The Tooltip has no public interface,
            // so set the value directly here to change the value of the tooltip
            // when content is successfully copied.
            scope.tt_content = 'Copied!';
            scope.$apply();
          });

          // Support for all other browsers without `execCommand`
          // support. Text will be selected and user will be prompted
          // to copy.
          clip.on('error', function() {
            scope.tt_content_orig = scope.tt_content;
            scope.tt_content = 'Press Ctrl/Cmd + C to copy!';
            scope.$apply();
          });
        }
      };
    }])
    .directive('mTimestamp', [ '$rootScope', function($rootScope) {
      return {
        restrict: 'E',
        transclude: true,
        scope: {
          value: '@'
        },
        link: function($scope, element, attrs) {
          $scope.longDate = JSON.parse(
            localStorage.getItem('longDate') || false);

          $scope.$on('mTimestamp.toggle', function() {
            $scope.longDate = !$scope.longDate;
          });

          $scope.toggle = function() {
            localStorage.setItem('longDate', !$scope.longDate);
            $rootScope.$broadcast('mTimestamp.toggle');
          };
        },
        templateUrl: 'static/directives/timestamp.html'
      }
    }])
    .directive('mPagination', function() {
      return { templateUrl: 'static/directives/pagination.html' }
    })
    .directive('mTableHeader', function() {
      return { templateUrl: 'static/directives/tableHeader.html' }
    })
    .directive('mTable', ['$compile', '$filter', function($compile, $filter) {
      /* This directive does not have a template. The DOM doesn't like
       * having partially defined tables and so they don't work well with
       * directives and templates. Because of this, the sub-elements that this
       * includes are their own directive/templates and it adds them via. DOM
       * manipulation here.
       */
      return {
        scope: true,
        link: function(scope, element, attrs) {
          var defaultOrder = true;

          _.extend(scope, {
            originalData: [],
            columnKey: '',
            sortOrder: defaultOrder,
            pgNum: 1,
            pageLength: 50,
            filterTerm: '',
            headerTitle: attrs.title
          })
          // ---

          // --- Allow sorting by column based on the  data-key attr
          var th = element.find('th');
          th.attr('ng-click', 'sortColumn($event)');
          $compile(th)(scope);

          var setSorting = function(el) {
            var key = el.attr('data-key');

            // Prevent sorting when 'data-key' is undefined.
            if (!key) {
              return;
            }

            if (scope.columnKey === key) {
              scope.sortOrder = !scope.sortOrder;
            }
            else { scope.sortOrder = defaultOrder }

            scope.columnKey = key;

            th.removeClass('descending ascending');
            el.addClass(scope.sortOrder ? 'descending' : 'ascending');
          };

          var defaultSortColumn = function() {
            var el = element.find('[data-sort]');
            if (el.length === 0) {
              el = element.find('th:first');
            }
            return el;
          };

          scope.sortColumn = function(ev) {
            setSorting(angular.element(ev.target));
          };

          setSorting(defaultSortColumn());
          // ---

          scope.$watch(attrs.tableContent, function(data) {
            if (!data) { scope.originalData = []; return }
            if (angular.isObject(data)) { data = _.values(data) }

            scope.originalData = data;
          });

          var setTableData = function() {
            scope.filteredData = $filter('filter')(scope.originalData, scope.filterTerm)
            scope.$data = $filter('orderBy')(
              scope.filteredData,
              scope.columnKey,
              scope.sortOrder).slice(
                (scope.pgNum - 1) * scope.pageLength,
                scope.pgNum * scope.pageLength);
          };

          // Reset the page number for each new filtering.
          scope.$watch('filterTerm', function() { scope.pgNum = 1; });

          _.each(['originalData', 'columnKey', 'sortOrder', 'pgNum', 'filterTerm'],
            function(k) { scope.$watch(k, setTableData); });

          // --- Pagination controls
          var el = angular.element('

');
          $compile(el)(scope);
          element.after(el);
          // ---

          // --- Filtering
          el = angular.element('

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

(function() {
  'use strict';

  angular.module('mesos', ['ngRoute', 'mesos.services', 'ui.bootstrap', 'ui.bootstrap.dialog']).
    config(['paginationConfig', '$routeProvider', function(paginationConfig, $routeProvider) {
      $routeProvider
        .when('/',
          {templateUrl: 'static/home.html', controller: 'HomeCtrl'})
        .when('/agents',
          {templateUrl: 'static/agents.html', controller: 'AgentsCtrl'})
        .when('/agents/:agent_id',
          {templateUrl: 'static/agent.html', controller: 'AgentCtrl'})
        .when('/agents/:agent_id/frameworks/:framework_id',
          {templateUrl: 'static/agent_framework.html', controller: 'AgentFrameworkCtrl'})
        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id',
          {templateUrl: 'static/agent_executor.html', controller: 'AgentExecutorCtrl'})
        .when('/frameworks',
          {templateUrl: 'static/frameworks.html', controller: 'FrameworksCtrl'})
        .when('/frameworks/:id',
          {templateUrl: 'static/framework.html', controller: 'FrameworkCtrl'})
        .when('/maintenance',
          {templateUrl: 'static/maintenance.html', controller: 'MaintenanceCtrl'})
        .when('/offers',
          {templateUrl: 'static/offers.html', controller: 'OffersCtrl'})
        .when('/roles',
          {templateUrl: 'static/roles.html', controller: 'RolesCtrl'})

        // TODO(tomxing): Remove the following '/slaves/*' paths once the
        // slave->agent rename is complete(MESOS-3779).
        .when('/slaves', {redirectTo: '/agents'})
        .when('/slaves/:agent_id', {redirectTo: '/agents/:agent_id'})
        .when('/slaves/:agent_id/frameworks/:framework_id',
          {redirectTo: '/agents/:agent_id/frameworks/:framework_id'})
        .when('/slaves/:agent_id/frameworks/:framework_id/executors/:executor_id',
          {redirectTo: '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id'})

        // Use a non-falsy template so the controller will still be executed.
        // Since the controller is intended only to redirect, the blank template
        // is fine.
        //
        // By design, controllers currently will not handle routes if the
        // template is falsy. There is an issue open in Angular to add that
        // feature:
        //
        //     https://github.com/angular/angular.js/issues/1838
        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse',
          {template: ' ', controller: 'AgentTaskAndExecutorRerouterCtrl'})
        .when('/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/tasks/:task_id/browse',
          {template: ' ', controller: 'AgentTaskAndExecutorRerouterCtrl'})
        .when('/agents/:agent_id/browse',
          {templateUrl: 'static/browse.html', controller: 'BrowseCtrl'})

        // TODO(tomxing): Remove the following '/slaves/*' paths once the
        // slave->agent rename is complete(MESOS-3779).
        .when('/slaves/:agent_id/frameworks/:framework_id/executors/:executor_id/browse',
          {redirectTo: '/agents/:agent_id/frameworks/:framework_id/executors/:executor_id/browse'})
        .when('/slaves/:agent_id/browse',
          {redirectTo: '/agents/:agent_id/browse'})
        .otherwise({redirectTo: '/'});

      // Configure [Angular UI Pagination][1]:
      //   * Show first/last buttons
      //   * Show 50 items per page
      //   * Show "..." when there are pages beyond the max shown
      //
      // [1] http://angular-ui.github.io/bootstrap/#/pagination
      paginationConfig.boundaryLinks = true;
      paginationConfig.rotate = false;
    }])
    .filter('truncateMesosID', function() {
      // Returns a truncated ID, for example:
      // Input: 9d4b2f2b-a759-4458-bebf-7d3507a6f0ca-S9
      // Output: ...7d3507a6f0ca-S9
      //
      // Note that an ellipsis is used for display purposes.
      return function(id) {
        if (id) {
          var truncatedIdParts = id.split('-');

          if (truncatedIdParts.length > 4) {
            return '\u2026' + truncatedIdParts.splice(4).join('-');
          } else {
            return id;
          }
        } else {
          return '';
        }
      };
    })
    .filter('truncateMesosState', function() {
      return function(state) {
        // Remove the "TASK_" prefix.
        return state.substring(5);
      };
    })
    .filter('isoDate', function($filter) {
      return function(date) {
        var i = parseInt(date, 10);
        if (_.isNaN(i)) { return '' }
        return $filter('date')(i, 'yyyy-MM-ddTHH:mm:ssZ');
      };
    })
    .filter('relativeDate', function() {
      return function(date, refDate) {
        var i = parseInt(date, 10);
        if (_.isNaN(i)) { return '' }
        return relativeDate(i, refDate);
      };
    })
    .filter('slice', function() {
      return function(array, begin, end) {
        if (_.isArray(array)) {
          return array.slice(begin, end);
        }
      };
    })
    .filter('unixDate', function($filter) {
      return function(date) {
        if ((new Date(date)).getFullYear() == (new Date()).getFullYear()) {
          return $filter('date')(date, 'MMM dd HH:mm');
        } else {
          return $filter('date')(date, 'MMM dd yyyy');
        }
      };
    })
    // A filter that uses to convert small float number to decimal string.
    .filter('decimalFloat', function() {
      return function(num) {
        return num ? parseFloat(num.toFixed(4)).toString() : num;
      }
    })
    .filter('dataSize', function() {
      var BYTES_PER_KB = Math.pow(2, 10);
      var BYTES_PER_MB = Math.pow(2, 20);
      var BYTES_PER_GB = Math.pow(2, 30);
      var BYTES_PER_TB = Math.pow(2, 40);
      var BYTES_PER_PB = Math.pow(2, 50);
      // NOTE: Number.MAX_SAFE_INTEGER is 2^53 - 1

      return function(bytes) {
        if (bytes == null || isNaN(bytes)) {
          return '';
        } else if (bytes < BYTES_PER_KB) {
          return bytes.toFixed() + ' B';
        } else if (bytes < BYTES_PER_MB) {
          return (bytes / BYTES_PER_KB).toFixed() + ' KB';
        } else if (bytes < BYTES_PER_GB) {
          return (bytes / BYTES_PER_MB).toFixed() + ' MB';
        } else if (bytes < BYTES_PER_TB) {
          return (bytes / BYTES_PER_GB).toFixed(1) + ' GB';
        } else if (bytes < BYTES_PER_PB) {
          return (bytes / BYTES_PER_TB).toFixed(1) + ' TB';
        } else {
          return (bytes / BYTES_PER_PB).toFixed(1) + ' PB';
        }
      };
    })
    .directive('clipboard', [function() {
      return {
        restrict: 'A',
        scope: true,
        template: '',

        link: function(scope, element, attrs) {
          var clip = new Clipboard(element[0]);

          element.on('mouseenter', function() {
            element.addClass('clipboard-is-hover');
            element.triggerHandler('clipboardhover');
          });

          element.on('mouseleave', function() {
            // Restore tooltip content to its original value if it was
            // changed by this Clipboard instance.
            if (scope && scope.tt_content_orig) {
              scope.tt_content = scope.tt_content_orig;
              delete scope.tt_content_orig;
            }

            element.removeClass('clipboard-is-hover');
            element.triggerHandler('clipboardhover');
          });

          // Success for browsers with `execCommand` support.
          clip.on('success', function () {
            // Store the tooltip's original content so it can
            // be restored when the tooltip is hidden.
            scope.tt_content_orig = scope.tt_content;

            // Angular UI's Tooltip sets content on the element's scope in a
            // variable named 'tt_content'. The Tooltip has no public interface,
            // so set the value directly here to change the value of the tooltip
            // when content is successfully copied.
            scope.tt_content = 'Copied!';
            scope.$apply();
          });

          // Support for all other browsers without `execCommand`
          // support. Text will be selected and user will be prompted
          // to copy.
          clip.on('error', function() {
            scope.tt_content_orig = scope.tt_content;
            scope.tt_content = 'Press Ctrl/Cmd + C to copy!';
            scope.$apply();
          });
        }
      };
    }])
    .directive('mTimestamp', [ '$rootScope', function($rootScope) {
      return {
        restrict: 'E',
        transclude: true,
        scope: {
          value: '@'
        },
        link: function($scope, element, attrs) {
          $scope.longDate = JSON.parse(
            localStorage.getItem('longDate') || false);

          $scope.$on('mTimestamp.toggle', function() {
            $scope.longDate = !$scope.longDate;
          });

          $scope.toggle = function() {
            localStorage.setItem('longDate', !$scope.longDate);
            $rootScope.$broadcast('mTimestamp.toggle');
          };
        },
        templateUrl: 'static/directives/timestamp.html'
      }
    }])
    .directive('mPagination', function() {
      return { templateUrl: 'static/directives/pagination.html' }
    })
    .directive('mTableHeader', function() {
      return { templateUrl: 'static/directives/tableHeader.html' }
    })
    .directive('mTable', ['$compile', '$filter', function($compile, $filter) {
      /* This directive does not have a template. The DOM doesn't like
       * having partially defined tables and so they don't work well with
       * directives and templates. Because of this, the sub-elements that this
       * includes are their own directive/templates and it adds them via. DOM
       * manipulation here.
       */
      return {
        scope: true,
        link: function(scope, element, attrs) {
          var defaultOrder = true;

          _.extend(scope, {
            originalData: [],
            columnKey: '',
            sortOrder: defaultOrder,
            pgNum: 1,
            pageLength: 50,
            filterTerm: '',
            headerTitle: attrs.title
          })
          // ---

          // --- Allow sorting by column based on the  data-key attr
          var th = element.find('th');
          th.attr('ng-click', 'sortColumn($event)');
          $compile(th)(scope);

          var setSorting = function(el) {
            var key = el.attr('data-key');

            // Prevent sorting when 'data-key' is undefined.
            if (!key) {
              return;
            }

            if (scope.columnKey === key) {
              scope.sortOrder = !scope.sortOrder;
            }
            else { scope.sortOrder = defaultOrder }

            scope.columnKey = key;

            th.removeClass('descending ascending');
            el.addClass(scope.sortOrder ? 'descending' : 'ascending');
          };

          var defaultSortColumn = function() {
            var el = element.find('[data-sort]');
            if (el.length === 0) {
              el = element.find('th:first');
            }
            return el;
          };

          scope.sortColumn = function(ev) {
            setSorting(angular.element(ev.target));
          };

          setSorting(defaultSortColumn());
          // ---

          scope.$watch(attrs.tableContent, function(data) {
            if (!data) { scope.originalData = []; return }
            if (angular.isObject(data)) { data = _.values(data) }

            scope.originalData = data;
          });

          var setTableData = function() {
            scope.filteredData = $filter('filter')(scope.originalData, scope.filterTerm)
            scope.$data = $filter('orderBy')(
              scope.filteredData,
              scope.columnKey,
              scope.sortOrder).slice(
                (scope.pgNum - 1) * scope.pageLength,
                scope.pgNum * scope.pageLength);
          };

          // Reset the page number for each new filtering.
          scope.$watch('filterTerm', function() { scope.pgNum = 1; });

          _.each(['originalData', 'columnKey', 'sortOrder', 'pgNum', 'filterTerm'],
            function(k) { scope.$watch(k, setTableData); });

          // --- Pagination controls
          var el = angular.element('

');
          $compile(el)(scope);
          element.after(el);
          // ---

          // --- Filtering
          el = angular.element('

');
          $compile(el)(scope);
          element.before(el);
          // ---
        }
      };
     }]);
})();
<|endoftext|>