<commit_before>var rescuers = [], listeners = [], push = [].push
var events = require('events')

function Delta (callback) {
    if (!(this instanceof Delta)) {
        return new Delta(callback)
    }
    this._callback = callback
    this._results = []
    this._waiting = 0
    this._listeners = []
    this._completed = false
    this._canceled = 0
}

Delta.prototype.ee = function (ee) {
    return new Constructor(this, ee)
}

Delta.prototype._unlisten = function (listener) {
    listener.f = null
    listener.ee.removeListener(listener.name, listener.listener)
    listener.heap.push(listener)
}

Delta.prototype.off = function (ee, name, f) {
    var listeners = this._listeners, i = 0, I = listeners.length, listener
    while (i < I) {
        listener = listeners[i]
        if (
            ee === listener.ee &&
            (!name || name == listener.name) &&
            (!f || f === listener.f)
        ) {
            if (listener.action === get && !--this._waiting) {
                this._done()
                break
            }
            this._unlisten(listener)
            listeners.splice(i, 1)
            I--
        } else {
            i++
        }
    }
}

Delta.prototype.cancel = function (vargs) {
    if (!this._completed) {
        this._canceled++
        this._listeners.forEach(unlisten)
        this._listeners.length = 0
        this._callback.apply(null, vargs)
        this._completed = true
    }
}

function unlisten (listener) {
    if (listener.ee != null || typeof listener.ee.removeListener == 'function') {
        listener.f = null
        listener.ee.removeListener(listener.name, listener.listener)
        listener.heap.push(listener)
    } else {
        var stackTraceLimit = Error.stackTraceLimit
        Error.stackTraceLimit = Infinity
        console.log(new Error().stack)
        Error.stackTraceLimit = stackTraceLimit
        console.log(listener)
        console.log(listener.ee)
        console.log(typeof listener.ee)
        console.log(listener.ee instanceof events.EventEmitter)
        if (listener.ee != null && typeof listener.ee == 'object') {
            console.log(listener.ee.constructor.name)
        }
    }
}

Delta.prototype._rescue = function (error, ee) {
    error.ee = ee
    this._listeners.forEach(unlisten)
    this._listeners.length = 0
    this._callback.call(null, error)
}

Delta.prototype._done = function () {
    var vargs = []
    for (var i = 0, I = this._results.length; i < I; i++) {
        push.apply(vargs, this._results[i])
    }
    if (vargs.length) {
        vargs.unshift(null)
    }
    this._listeners.forEach(unlisten)
    this._listeners.length = 0
    this._callback.apply(null, vargs)
    this._completed = true
}

function Constructor (delta, ee) {
    var rescuer = rescuers.pop()
    if (rescuer == null) {
        rescuer = {
            delta: delta,
            ee: ee,
            name: 'error',
            listener: function (error) {
                rescuer.delta._rescue(error, rescuer.ee)
                rescuers.push(rescuer)
            },
            heap: rescuers
        }
    } else {
        rescuer.delta = delta
        rescuer.ee = ee
    }

    delta._listeners.push(rescuer)

    ee.on('error', rescuer.listener)

    this._delta = delta
    this._ee = ee
}

function gather (vargs) {
    push.apply(this.delta._results[this.index][0], vargs)
}

function get (vargs) {
    push.apply(this.delta._results[this.index], vargs)
    this.delta.off(this.ee, this.name)
}

function invoke (vargs) {
    try {
        this.f.apply(null, vargs)
    } catch (error) {
        this.delta._rescue(error)
    }
}

Constructor.prototype.on = function (name, object) {
    var listener = listeners.pop()

    if (listener == null) {
        listener = {
            delta: this,
            ee: this._ee,
            name: name,
            action: null,
            listening: true,
            index: 0,
            f: null,
            listener: function () {
                var vargs = new Array
                for (var i = 0, I = arguments.length; i < I; i++) {
                    vargs[i] = arguments[i]
                }
                listener.action(vargs)
            },
            heap: listeners
        }
    }

    listener.delta = this._delta
    listener.ee = this._ee
    listener.name = name

    if (Array.isArray(object)) {
        this._delta._results.push([[]])
        listener.index = this._delta._results.length - 1
        listener.action = gather
    } else if (typeof object == 'function') {
        listener.action = invoke
        listener.f = object
    } else {
        this._delta._results.push([])
        this._delta._waiting++
        listener.index = this._delta._results.length - 1
        listener.action = get
    }

    this._delta._listeners.push(listener)
    this._ee.on(name, listener.listener)

    return this
}

Constructor.prototype.ee = function (ee) {
    return new Constructor(this._delta, ee)
}

Constructor.prototype.cancel = function (vargs) {
    this._delta.cancel(vargs)
}

module.exports = Delta
<commit_msg>More missing function debugging.<commit_after>var rescuers = [], listeners = [], push = [].push
var events = require('events')

function Delta (callback) {
    if (!(this instanceof Delta)) {
        return new Delta(callback)
    }
    this._callback = callback
    this._results = []
    this._waiting = 0
    this._listeners = []
    this._completed = false
    this._canceled = 0
    this._instance = INSTANCE++
}

Delta.prototype.ee = function (ee) {
    return new Constructor(this, ee)
}

Delta.prototype._unlisten = function () {
    this._listeners.forEach(function (listener) {
        unlisten(listener, this)
    }, this)
    this._listeners.length = 0
}

Delta.prototype.off = function (ee, name, f) {
    var listeners = this._listeners, i = 0, I = listeners.length, listener
    while (i < I) {
        listener = listeners[i]
        if (
            ee === listener.ee &&
            (!name || name == listener.name) &&
            (!f || f === listener.f)
        ) {
            if (listener.action === get && !--this._waiting) {
                this._done()
                break
            }
            unlisten(listener, this)
            listeners.splice(i, 1)
            I--
        } else {
            i++
        }
    }
}

Delta.prototype.cancel = function (vargs) {
    if (!this._completed) {
        this._canceled++
        this._listeners.forEach(unlisten)
        this._listeners.length = 0
        this._callback.apply(null, vargs)
        this._completed = true
    }
}

function unlisten (listener, delta) {
    if (listener.ee != null || typeof listener.ee.removeListener == 'function') {
        listener.f = null
        listener.ee.removeListener(listener.name, listener.listener)
        listener.heap.push(listener)
    } else {
        console.log('no removeListener')
        var stackTraceLimit = Error.stackTraceLimit
        Error.stackTraceLimit = Infinity
        console.log(new Error().stack)
        Error.stackTraceLimit = stackTraceLimit
        console.log('delta replaced', delta === listener.delta)
        console.log(listener)
        console.log(listener.ee)
        console.log(typeof listener.ee)
        console.log(listener.ee instanceof events.EventEmitter)
        if (listener.ee != null && typeof listener.ee == 'object') {
            console.log(listener.ee.constructor.name)
            console.log(listener.ee)
        }
    }
}

Delta.prototype._rescue = function (error, ee) {
    error.ee = ee
    this._unlisten()
    this._callback.call(null, error)
}

Delta.prototype._done = function () {
    var vargs = []
    for (var i = 0, I = this._results.length; i < I; i++) {
        push.apply(vargs, this._results[i])
    }
    if (vargs.length) {
        vargs.unshift(null)
    }
    this._unlisten()
    this._callback.apply(null, vargs)
    this._completed = true
}

var INSTANCE = 0

function Constructor (delta, ee) {
    this._instance = INSTANCE++

    var rescuer = rescuers.pop()
    if (rescuer == null) {
        rescuer = {
            instance: this._instance,
            delta: delta,
            ee: ee,
            name: 'error',
            listener: function (error) {
                rescuer.delta._rescue(error, rescuer.ee)
                rescuers.push(rescuer)
            },
            heap: rescuers
        }
    } else {
        rescuer.delta = delta
        rescuer.ee = ee
        rescuer.instance = this._instance
    }

    delta._listeners.push(rescuer)

    ee.on('error', rescuer.listener)

    this._delta = delta
    this._ee = ee
}

function gather (vargs) {
    push.apply(this.delta._results[this.index][0], vargs)
}

function get (vargs) {
    push.apply(this.delta._results[this.index], vargs)
    this.delta.off(this.ee, this.name)
}

function invoke (vargs) {
    try {
        this.f.apply(null, vargs)
    } catch (error) {
        this.delta._rescue(error)
    }
}

Constructor.prototype.on = function (name, object) {
    var listener = listeners.pop()

    if (listener == null) {
        listener = {
            instance: this._instance,
            delta: this,
            ee: this._ee,
            name: name,
            action: null,
            listening: true,
            index: 0,
            f: null,
            listener: function () {
                var vargs = new Array
                for (var i = 0, I = arguments.length; i < I; i++) {
                    vargs[i] = arguments[i]
                }
                listener.action(vargs)
            },
            heap: listeners
        }
    }

    listener.delta = this._delta
    listener.ee = this._ee
    listener.name = name
    listener.instance = this._instance

    if (Array.isArray(object)) {
        this._delta._results.push([[]])
        listener.index = this._delta._results.length - 1
        listener.action = gather
    } else if (typeof object == 'function') {
        listener.action = invoke
        listener.f = object
    } else {
        this._delta._results.push([])
        this._delta._waiting++
        listener.index = this._delta._results.length - 1
        listener.action = get
    }

    this._delta._listeners.push(listener)
    this._ee.on(name, listener.listener)

    return this
}

Constructor.prototype.ee = function (ee) {
    return new Constructor(this._delta, ee)
}

Constructor.prototype.cancel = function (vargs) {
    this._delta.cancel(vargs)
}

module.exports = Delta
<|endoftext|>


<commit_before>/*  HashNav is ...
    It was inspired by https://github.com/ccoenraets/PageSlider

    @note: this library depends on jquery core.

    General Usage:
        var hashnav = new HashNav(container_id, options);

    Example use:
        var hashnav = new HashNav('container');
        hashnav.add_route("/foo/", {top_level_tab_index: 0}, function(a){ return '
Foo
'; };
        hashnav.add_route("/foo/(.*)/bar", function(a){ return '
Foo: ' + a + '
'; })
        hashnav.add_route("/foo/(.*)/bar", function(a){ return '
Bar: ' + a + '
'; })
        hashnav.add_route("/foo/(.*)/baz", function(a){ return '
Baz: ' + a + '
'; })

    Options:
     - allow_reloads -- regenerate same page twice in a row; this can also be
        specified per route; possible use case is if route involves querying
        of possibly chnging data from server
     - always_transition_top_level_pages_from_center -- Don't use left/right
        transition effect when going from one top level tab
     - after_render -- callback to be executed after rendering any route
        (called before any route-specific after-render callback)
*/

function HashNav(container_id, options) {
    options = options || {};

    var history = [];
    var routes = [];

    var path_param_matcher = /:[^/]+/g;

    /** Public Interface **/

    /*  add_route: associates routes matching a pattern with a content generator.
        @todo: switch 'generator' and 'options' args and support missing options

        Options:
         - allow_reloads -- same meaning as global option of same name, except
            specific to this route.
         - top_level_tab_index -- use for animating left/right transition between
            top level tabs
         - after_render -- callback to be executed after rendering this route
            (called after any global after-render callback)
    */
    this.add_route = function(pattern, route_options, generator) {
        if (typeof route_options == 'function') {
            generator = route_options;
            route_options = {};
        } else {
            route_options = route_options || {};
        }

        //pattern = pattern.replace(/\/*$/, ''); /* so that we match with or with trailing slash */

        pattern = pattern.replace(path_param_matcher, '([^/]+)');

        var r = {
            matcher: RegExp("^" + pattern + "$"),
            generator: generator
        };
        if (typeof route_options.top_level_tab_index != 'undefined') {
            r.top_level_tab_index = route_options.top_level_tab_index;
        }
        if (typeof route_options.after_render != 'undefined') {
            r.after_render = route_options.after_render;
        }
        routes.push(r);
    }

    var leading_hash_matcher = /^#/;

    /*  go: navigate to the current page
    */
    this.go = function() {
        var page = window.location.hash.replace(leading_hash_matcher, '');

        for (var i = 0; i < routes.length; i++) {
            var m = page.match(routes[i].matcher);
            if (m){
                var args = m.slice(1,m.length);
                var content = routes[i].generator.apply(null, args);
                navigate(page, content, routes[i]);
                return;
            }
        }
        // if we get here, it's effectively a 404, which we'll just ignore
        // TOOD: support options.four_oh_four
    }

    /*  back: navigate to the previously visited page
    */
    this.back = function() {
        var l = history.length;

        if (l > 1) {
            /* pop current page */
            //history.pop()
            /* go previous page */
            //navigate(history[l-2].p, history[l-2].c, history[l-2].r);
            window.location.hash = history[l-2].p;
        }
        // else, silently do nothing
        // TODO: is this appropriate behavior?
    }

    var call_after_render = function(route) {
        if (options.after_render) {
            options.after_render();
        }
        if (route.after_render) {
            route.after_render();
        }
    }

    /** Private Methods **/

    var navigate = function(page, content, route) {
        // TODO: restructure code to move history modification, calls
        // to slide, and calling after_render callback in separate function
        var l = history.length;

        if (l === 0) {  /* No history */
            history.push({p: page, r: route});
            slide(content);
        } else if (history[l-1].p === page) {  /* Same page */
            if (options.allow_reloads || route.allow_reloads) {
                /* Don't push to history, but reload content */
                slide(content);
            } else {
                // trying visit same page, but we're ignoring reloads
                return;
            }
        } else if (l > 1 && page === history[l-2].p) { /* Previous page */
            /* Slide from the left and pop current page from history */
            history.pop();
            slide(content, 'left');
        } else if (typeof route.top_level_tab_index != 'undefined') {
            /* It's one of the top level pages; clear history and add current
               page to it, and then transition according to previously visited
               top-level page and use options */
            var t_idx = history[0].r.top_level_tab_index;
            if (typeof t_idx === 'undefined' || t_idx === route.top_level_tab_index ||
                    options.always_transition_top_level_pages_from_center) {
                slide(content);
            } else {
                var direction = (route.top_level_tab_index < t_idx) ? ('left') : ('right');
                slide(content, direction);
            }
            history = [{p: page, r: route}];
        } else {
            /* New page to push on history stack */
            history.push({p: page, r: route});
            slide(content, 'right');
        }

        call_after_render(route);
    }

    var first_page = true;

    var slide = function(content, from) {
        if (first_page) {
            /* Insert content with no transition effect */
            $('#' + container_id).html(content);
            first_page = false;
        } else {
            var old_container = $('#' + container_id);
            old_container.attr('id', container_id + guid());

            add_new_content(content, from, old_container);
            var new_container = $('#' + container_id);

            // Force reflow. More information here:
            // http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
            old_container.offsetWidth;
            new_container.offsetWidth;

            transition(from, old_container, new_container);
        }

    }

    var guid = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    }

    var add_new_content = function(content, from, old_container) {
        var cls = "hashnav-page";

        /* add class to position left of, right of, or below viewable region */
        if (from === 'left') {
            cls += ' hashnav-left';
        } else if (from === 'right') {
            cls += ' hashnav-right';
        } else {
            cls += ' hashnav-below';
        }

        /* add container's original classes */
        cls += ' ' + old_container.attr('class');

        old_container.after('
' + content + '
');
    }

    var transition = function(from, old_container, new_container) {
        var container_class = old_container.attr('class');

        old_container.one('webkitTransitionEnd', function(e) {
            $('#' + old_container.attr('id')).remove();
            new_container.attr('class', container_class);
        });
        // new_container.one('webkitTransitionEnd', function(e) {
        //     new_container.attr('class', container_class)
        // })

        old_container.attr('class', [container_class, old_container_class(from)].join(' '));
        new_container.attr('class', [container_class, new_container_class()].join(' '));
    }

    var old_container_class = function(from) {
        var cls = 'hashnav-page hashnav-transition';
        if (from === 'left') {
            cls += ' hashnav-right';
        } else if (from === 'right') {
            cls += ' hashnav-left';
        } else {
            cls += ' hashnav-above';
        }

        return cls;
    }

    var new_container_class = function() {
        return 'hashnav-page hashnav-transition hashnav-center';
    }

    $(window).on('hashchange', this.go);
}
<commit_msg>Use setTimeout rather than regisering callback for  webkitTransitionEnd event<commit_after>/*  HashNav is ...
    It was inspired by https://github.com/ccoenraets/PageSlider

    @note: this library depends on jquery core.

    General Usage:
        var hashnav = new HashNav(container_id, options);

    Example use:
        var hashnav = new HashNav('container');
        hashnav.add_route("/foo/", {top_level_tab_index: 0}, function(a){ return '
Foo
'; };
        hashnav.add_route("/foo/(.*)/bar", function(a){ return '
Foo: ' + a + '
'; })
        hashnav.add_route("/foo/(.*)/bar", function(a){ return '
Bar: ' + a + '
'; })
        hashnav.add_route("/foo/(.*)/baz", function(a){ return '
Baz: ' + a + '
'; })

    Options:
     - allow_reloads -- regenerate same page twice in a row; this can also be
        specified per route; possible use case is if route involves querying
        of possibly chnging data from server
     - always_transition_top_level_pages_from_center -- Don't use left/right
        transition effect when going from one top level tab
     - after_render -- callback to be executed after rendering any route
        (called before any route-specific after-render callback)
*/

function HashNav(container_id, options) {
    options = options || {};

    var history = [];
    var routes = [];

    var path_param_matcher = /:[^/]+/g;

    /** Public Interface **/

    /*  add_route: associates routes matching a pattern with a content generator.
        @todo: switch 'generator' and 'options' args and support missing options

        Options:
         - allow_reloads -- same meaning as global option of same name, except
            specific to this route.
         - top_level_tab_index -- use for animating left/right transition between
            top level tabs
         - after_render -- callback to be executed after rendering this route
            (called after any global after-render callback)
    */
    this.add_route = function(pattern, route_options, generator) {
        if (typeof route_options == 'function') {
            generator = route_options;
            route_options = {};
        } else {
            route_options = route_options || {};
        }

        //pattern = pattern.replace(/\/*$/, ''); /* so that we match with or with trailing slash */

        pattern = pattern.replace(path_param_matcher, '([^/]+)');

        var r = {
            matcher: RegExp("^" + pattern + "$"),
            generator: generator
        };
        if (typeof route_options.top_level_tab_index != 'undefined') {
            r.top_level_tab_index = route_options.top_level_tab_index;
        }
        if (typeof route_options.after_render != 'undefined') {
            r.after_render = route_options.after_render;
        }
        routes.push(r);
    }

    var leading_hash_matcher = /^#/;

    /*  go: navigate to the current page
    */
    this.go = function() {
        var page = window.location.hash.replace(leading_hash_matcher, '');

        for (var i = 0; i < routes.length; i++) {
            var m = page.match(routes[i].matcher);
            if (m){
                var args = m.slice(1,m.length);
                var content = routes[i].generator.apply(null, args);
                navigate(page, content, routes[i]);
                return;
            }
        }
        // if we get here, it's effectively a 404, which we'll just ignore
        // TOOD: support options.four_oh_four
    }

    /*  back: navigate to the previously visited page
    */
    this.back = function() {
        var l = history.length;

        if (l > 1) {
            /* pop current page */
            //history.pop()
            /* go previous page */
            //navigate(history[l-2].p, history[l-2].c, history[l-2].r);
            window.location.hash = history[l-2].p;
        }
        // else, silently do nothing
        // TODO: is this appropriate behavior?
    }

    var call_after_render = function(route) {
        if (options.after_render) {
            options.after_render();
        }
        if (route.after_render) {
            route.after_render();
        }
    }

    /** Private Methods **/

    var navigate = function(page, content, route) {
        // TODO: restructure code to move history modification, calls
        // to slide, and calling after_render callback in separate function
        var l = history.length;

        if (l === 0) {  /* No history */
            history.push({p: page, r: route});
            slide(content);
        } else if (history[l-1].p === page) {  /* Same page */
            if (options.allow_reloads || route.allow_reloads) {
                /* Don't push to history, but reload content */
                slide(content);
            } else {
                // trying visit same page, but we're ignoring reloads
                return;
            }
        } else if (l > 1 && page === history[l-2].p) { /* Previous page */
            /* Slide from the left and pop current page from history */
            history.pop();
            slide(content, 'left');
        } else if (typeof route.top_level_tab_index != 'undefined') {
            /* It's one of the top level pages; clear history and add current
               page to it, and then transition according to previously visited
               top-level page and use options */
            var t_idx = history[0].r.top_level_tab_index;
            if (typeof t_idx === 'undefined' || t_idx === route.top_level_tab_index ||
                    options.always_transition_top_level_pages_from_center) {
                slide(content);
            } else {
                var direction = (route.top_level_tab_index < t_idx) ? ('left') : ('right');
                slide(content, direction);
            }
            history = [{p: page, r: route}];
        } else {
            /* New page to push on history stack */
            history.push({p: page, r: route});
            slide(content, 'right');
        }

        call_after_render(route);
    }

    var first_page = true;

    var slide = function(content, from) {
        if (first_page) {
            /* Insert content with no transition effect */
            $('#' + container_id).html(content);
            first_page = false;
        } else {
            var old_container = $('#' + container_id);
            old_container.attr('id', container_id + guid());

            add_new_content(content, from, old_container);
            var new_container = $('#' + container_id);

            // Force reflow. More information here:
            // http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
            old_container.offsetWidth;
            new_container.offsetWidth;

            transition(from, old_container, new_container);
        }

    }

    var guid = function() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    }

    var add_new_content = function(content, from, old_container) {
        var cls = "hashnav-page";

        /* add class to position left of, right of, or below viewable region */
        if (from === 'left') {
            cls += ' hashnav-left';
        } else if (from === 'right') {
            cls += ' hashnav-right';
        } else {
            cls += ' hashnav-below';
        }

        /* add container's original classes */
        cls += ' ' + old_container.attr('class');

        old_container.after('
' + content + '
');
    }

    var transition = function(from, old_container, new_container) {
        var container_class = old_container.attr('class');
        /* Note: this uses setTimeout rather than
             old_container.one('webkitTransitionEnd', function(e) {...}
           because hitting links in quick succession, before the previous
           transition's end, was resulting in orphaned old containers
           being left behind */

        old_container.attr('class', [container_class, old_container_class(from)].join(' '));
        new_container.attr('class', [container_class, new_container_class()].join(' '));
        setTimeout(function(){
            old_container.remove();
            new_container.attr('class', container_class);
        }, 1250);
    }

    var old_container_class = function(from) {
        var cls = 'hashnav-page hashnav-transition';
        if (from === 'left') {
            cls += ' hashnav-right';
        } else if (from === 'right') {
            cls += ' hashnav-left';
        } else {
            cls += ' hashnav-above';
        }

        return cls;
    }

    var new_container_class = function() {
        return 'hashnav-page hashnav-transition hashnav-center';
    }

    $(window).on('hashchange', this.go);
}
<|endoftext|>


<commit_before>/* eslint-disable no-useless-escape */
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { isURL, isISBN } from 'validator';

const relationTypeList = [
  'IsCitedBy',
  'Cites',
  'IsSupplementTo',
  'IsSupplementedBy',
  'IsContinuedBy',
  'Continues',
  'IsDescribedBy',
  'Describes',
  'HasMetadata',
  'IsMetadataFor',
  'HasVersion',
  'IsVersionOf',
  'IsNewVersionOf',
  'IsPreviousVersionOf',
  'IsPartOf',
  'HasPart',
  'IsReferencedBy',
  'References',
  'IsDocumentedBy',
  'Documents',
  'IsCompiledBy',
  'Compiles',
  'IsVariantFormOf',
  'IsOriginalFormOf',
  'IsIdenticalTo',
  'IsReviewedBy',
  'Reviews',
  'IsDerivedFrom',
  'IsSourceOf',
  'IsRequiredBy',
  'Requires',
  'IsObsoletedBy',
  'Obsoletes',
];

const relatedIdentifierTypeList = [
  'ARK',
  'arXiv',
  'bibcode',
  'DOI',
  'EAN13',
  'EISSN',
  'Handle',
  'IGSN',
  'ISBN',
  'ISSN',
  'ISTC',
  'LISSN',
  'LSID',
  'PMID',
  'PURL',
  'UPC',
  'URL',
  'URN',
  'w3id',
];

const resourceTypeGeneralList = [
  'Audiovisual',
  'Collection',
  'DataPaper',
  'Dataset',
  'Event',
  'Image',
  'InteractiveResource',
  'Model',
  'PhysicalObject',
  'Service',
  'Software',
  'Sound',
  'Text',
  'Workflow',
  'Other',
];

export default Component.extend({
  store: service(),
  relationTypeList,
  relationTypes: relationTypeList,
  relatedIdentifierTypeList,
  relatedIdentifierTypes: relatedIdentifierTypeList,
  controlledIdentifierType: false,
  isMetadataRelationType: false,
  isMetadataRelationTypes: [ 'HasMetadata', 'IsMetadataFor' ],
  resourceTypeGeneralList,
  resourceTypesGeneral: resourceTypeGeneralList,

  didReceiveAttrs() {
    this._super(...arguments);

    if (relatedIdentifierTypeList.includes(this.fragment.get('relatedIdentifierType'))) {
      this.set('controlledIdentifierType', true);
    } else {
      this.set('controlledIdentifierType', false);
    }
    if (this.isMetadataRelationTypes.includes(this.fragment.get('relationType'))) {
      this.set('isMetadataRelationType', true);
    } else {
      this.set('isMetadataRelationType', false);
    }
  },
  updateRelatedIdentifier(value) {
    const ark = /^ark:\/[0-9]{5}\/\S+$/;
    const lsid = /^[uU][rR][nN]:[lL][sS][iI][dD]:(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%]):(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%]):(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%])[:]?(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%])?$/;
    const purl = /^http?:\/\/(purl\.oclc\.org\/)/;
    const arxiv = /^(arXiv:)(\d{4}.\d{4,5}|[a-z\-]+(\.[A-Z]{2})?\/\d{7})(v\d+)?/;
    const doi = /^(10\.\d{4,5}\/.+)/;
    const bibcode = /\d{4}[A-Za-z\.\&]{5}[\w\.]{4}[ELPQ-Z\.][\d\.]{4}[A-Z]/;
    const urn = /^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,\-.:=@;$_!*'%/?#]/;

    switch (true) {
      case value == null:
        this.fragment.set('relatedIdentifier', null);
        this.fragment.set('relatedIdentifierType', null);
        this.set('controlledIdentifierType', false);
        break;
      case ark.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'ARK');
        this.set('controlledIdentifierType', true);
        break;
      case arxiv.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'arXiv');
        this.set('controlledIdentifierType', true);
        break;
      case doi.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'DOI');
        this.set('controlledIdentifierType', true);
        break;
      case bibcode.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'bibcode');
        this.set('controlledIdentifierType', true);
        break;
      case lsid.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'LSID');
        this.set('controlledIdentifierType', true);
        break;
      case purl.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'PURL');
        this.set('controlledIdentifierType', true);
        break;
      case urn.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'URN');
        this.set('controlledIdentifierType', true);
        break;
      case isISBN(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'ISBN');
        this.set('controlledIdentifierType', true);
        break;
      // // EAN currently not supported https://github.com/validatorjs/validator.js/issues/797
      // case isEAN(value):
      //   this.fragment.set('relatedIdentifier', value);
      //   this.fragment.set('relatedIdentifierType', 'EAN13');
      //   this.set('controlledIdentifierType', true);
      //   break;
      case isURL(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'URL');
        this.set('controlledIdentifierType', true);
        break;
      default:
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', null);
        this.set('controlledIdentifierType', false);
        break;
    }
  },
  selectRelationType(relationType) {
    if (this.isMetadataRelationTypes.includes(relationType)) {
      this.set('isMetadataRelationType', true);
    } else {
      this.set('isMetadataRelationType', false);
    }
    this.fragment.set('relationType', relationType);
    this.set('relationTypes', relationTypeList);
  },
  selectRelatedIdentifierType(relatedIdentifierType) {
    this.fragment.set('relatedIdentifierType', relatedIdentifierType);
    this.set('relatedIdentifierTypes', relatedIdentifierTypeList);
  },
  selectResourceTypeGeneral(resourceTypeGeneral) {
    this.fragment.set('resourceTypeGeneral', resourceTypeGeneral);
    this.set('resourceTypesGeneral', resourceTypeGeneralList);
  },
  actions: {
    updateRelatedIdentifier(value) {
      this.updateRelatedIdentifier(value.trim());
    },
    updateRelatedMetadataScheme(value) {
      this.fragment.set('relatedMetadataScheme', value);
    },
    updateSchemeURI(value) {
      this.fragment.set('schemeUri', value);
    },
    updateSchemeType(value) {
      this.fragment.set('schemeType', value);
    },
    selectRelationType(relationType) {
      this.selectRelationType(relationType);
    },
    selectRelatedIdentifierType(relatedIdentifierType) {
      this.selectRelatedIdentifierType(relatedIdentifierType);
    },
    selectResourceTypeGeneral(resourceTypeGeneral) {
      this.selectResourceTypeGeneral(resourceTypeGeneral);
    },
    deleteRelatedIdentifier() {
      this.model.get('relatedIdentifiers').removeObject(this.fragment);
    },
  },
});
<commit_msg>sort the list of relation types<commit_after>/* eslint-disable no-useless-escape */
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { isURL, isISBN } from 'validator';

const relationTypeList = [
  'Cites',
  'IsCitedBy',
  'Compiles',
  'IsCompiledBy',
  'Continues',
  'IsContinuedBy',
  'Describes',
  'IsDescribedBy',
  'Documents',
  'IsDocumentedBy',
  'IsDerivedFrom',
  'IsSourceOf',
  'HasMetadata',
  'IsMetadataFor',
  'HasPart',
  'IsPartOf',
  'IsSupplementedBy',
  'IsSupplementTo',
  'Obsoletes',
  'IsObsoletedBy',
  'References',
  'IsReferencedBy',
  'Requires',
  'IsRequiredBy',
  'Reviews',
  'IsReviewedBy',
  'HasVersion',
  'IsVersionOf',
  'IsNewVersionOf',
  'IsPreviousVersionOf',
  'IsVariantFormOf',
  'IsOriginalFormOf',
  'IsIdenticalTo',
];

const relatedIdentifierTypeList = [
  'ARK',
  'arXiv',
  'bibcode',
  'DOI',
  'EAN13',
  'EISSN',
  'Handle',
  'IGSN',
  'ISBN',
  'ISSN',
  'ISTC',
  'LISSN',
  'LSID',
  'PMID',
  'PURL',
  'UPC',
  'URL',
  'URN',
  'w3id',
];

const resourceTypeGeneralList = [
  'Audiovisual',
  'Collection',
  'DataPaper',
  'Dataset',
  'Event',
  'Image',
  'InteractiveResource',
  'Model',
  'PhysicalObject',
  'Service',
  'Software',
  'Sound',
  'Text',
  'Workflow',
  'Other',
];

export default Component.extend({
  store: service(),
  relationTypeList,
  relationTypes: relationTypeList,
  relatedIdentifierTypeList,
  relatedIdentifierTypes: relatedIdentifierTypeList,
  controlledIdentifierType: false,
  isMetadataRelationType: false,
  isMetadataRelationTypes: [ 'HasMetadata', 'IsMetadataFor' ],
  resourceTypeGeneralList,
  resourceTypesGeneral: resourceTypeGeneralList,

  didReceiveAttrs() {
    this._super(...arguments);

    if (relatedIdentifierTypeList.includes(this.fragment.get('relatedIdentifierType'))) {
      this.set('controlledIdentifierType', true);
    } else {
      this.set('controlledIdentifierType', false);
    }
    if (this.isMetadataRelationTypes.includes(this.fragment.get('relationType'))) {
      this.set('isMetadataRelationType', true);
    } else {
      this.set('isMetadataRelationType', false);
    }
  },
  updateRelatedIdentifier(value) {
    const ark = /^ark:\/[0-9]{5}\/\S+$/;
    const lsid = /^[uU][rR][nN]:[lL][sS][iI][dD]:(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%]):(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%]):(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%])[:]?(A-Za-z0-9][A-Za-z0-9()+,-.=@;$_!*'"%])?$/;
    const purl = /^http?:\/\/(purl\.oclc\.org\/)/;
    const arxiv = /^(arXiv:)(\d{4}.\d{4,5}|[a-z\-]+(\.[A-Z]{2})?\/\d{7})(v\d+)?/;
    const doi = /^(10\.\d{4,5}\/.+)/;
    const bibcode = /\d{4}[A-Za-z\.\&]{5}[\w\.]{4}[ELPQ-Z\.][\d\.]{4}[A-Z]/;
    const urn = /^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,\-.:=@;$_!*'%/?#]/;

    switch (true) {
      case value == null:
        this.fragment.set('relatedIdentifier', null);
        this.fragment.set('relatedIdentifierType', null);
        this.set('controlledIdentifierType', false);
        break;
      case ark.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'ARK');
        this.set('controlledIdentifierType', true);
        break;
      case arxiv.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'arXiv');
        this.set('controlledIdentifierType', true);
        break;
      case doi.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'DOI');
        this.set('controlledIdentifierType', true);
        break;
      case bibcode.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'bibcode');
        this.set('controlledIdentifierType', true);
        break;
      case lsid.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'LSID');
        this.set('controlledIdentifierType', true);
        break;
      case purl.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'PURL');
        this.set('controlledIdentifierType', true);
        break;
      case urn.test(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'URN');
        this.set('controlledIdentifierType', true);
        break;
      case isISBN(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'ISBN');
        this.set('controlledIdentifierType', true);
        break;
      // // EAN currently not supported https://github.com/validatorjs/validator.js/issues/797
      // case isEAN(value):
      //   this.fragment.set('relatedIdentifier', value);
      //   this.fragment.set('relatedIdentifierType', 'EAN13');
      //   this.set('controlledIdentifierType', true);
      //   break;
      case isURL(value):
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', 'URL');
        this.set('controlledIdentifierType', true);
        break;
      default:
        this.fragment.set('relatedIdentifier', value);
        this.fragment.set('relatedIdentifierType', null);
        this.set('controlledIdentifierType', false);
        break;
    }
  },
  selectRelationType(relationType) {
    if (this.isMetadataRelationTypes.includes(relationType)) {
      this.set('isMetadataRelationType', true);
    } else {
      this.set('isMetadataRelationType', false);
    }
    this.fragment.set('relationType', relationType);
    this.set('relationTypes', relationTypeList);
  },
  selectRelatedIdentifierType(relatedIdentifierType) {
    this.fragment.set('relatedIdentifierType', relatedIdentifierType);
    this.set('relatedIdentifierTypes', relatedIdentifierTypeList);
  },
  selectResourceTypeGeneral(resourceTypeGeneral) {
    this.fragment.set('resourceTypeGeneral', resourceTypeGeneral);
    this.set('resourceTypesGeneral', resourceTypeGeneralList);
  },
  actions: {
    updateRelatedIdentifier(value) {
      this.updateRelatedIdentifier(value.trim());
    },
    updateRelatedMetadataScheme(value) {
      this.fragment.set('relatedMetadataScheme', value);
    },
    updateSchemeURI(value) {
      this.fragment.set('schemeUri', value);
    },
    updateSchemeType(value) {
      this.fragment.set('schemeType', value);
    },
    selectRelationType(relationType) {
      this.selectRelationType(relationType);
    },
    selectRelatedIdentifierType(relatedIdentifierType) {
      this.selectRelatedIdentifierType(relatedIdentifierType);
    },
    selectResourceTypeGeneral(resourceTypeGeneral) {
      this.selectResourceTypeGeneral(resourceTypeGeneral);
    },
    deleteRelatedIdentifier() {
      this.model.get('relatedIdentifiers').removeObject(this.fragment);
    },
  },
});
<|endoftext|>


<commit_before>var ServiceWorker = {
	onServiceWorkerInstalled: function(event) {
		event.waitUntil(self.skipWaiting());
	},

	onServiceWorkerActivated: function(event) {
    event.waitUntil(self.clients.claim());
	},

	onPush: function(event) {
		var payload = event.data.json();

		event.waitUntil(async function(){
			try {
				var json = JSON.parse(payload.aps.alert);
				var message = {};
				var title = json.title || '';

				if(json.hasOwnProperty('body')) {
					message.body = json.body;
				}
				message.data = payload;

				DB.get("Options", "options").then((local) => {
					if(json.hasOwnProperty('icon')) {
						message.icon = json.icon;
					} else if(local.hasOwnProperty("icon")) {
						message.icon = local.icon;
					}

					self.registration.showNotification(title, message);
				});
			} catch(e) {
				DB.get("Options", "options").then((local) => {
					var message = {};
					var title = local.title || '';

					if(local.hasOwnProperty('icon')) {
						message.icon = local.icon;
					}

					message.body = payload.aps.alert;
					message.data = payload;

					self.registration.showNotification(title, message);
				});
			}

			// Send ACK to UPMC
			ServiceWorker.sendIF('/rcv_register_sent_ack.ctl', { SEQNO: payload.mps.seqno }).catch(function(e) {});
		}());
	},

	onNotificationClicked: function(event) {
		event.waitUntil(async function() {
			var option = await DB.get("Options", "options").catch(function(e) {}) || {};
			var launchUrl = option.url || self.registration.scope;
			var seqno = event.notification.data.mps.seqno;

			event.notification.close();

			if(event.notification.data) {
				try {
					var json = JSON.parse(event.notification.data.aps.alert);
					if(json.hasOwnProperty('url')) {
						launchUrl = json.url;
					}
				} catch(e) {
				}
			}

			clients.openWindow(launchUrl);

			// Send Read to UPMC
			ServiceWorker.sendIF('/rcv_register_send_result.ctl', { SEQNO: seqno }).catch(function(e) {});
		}());
	},

	run: function() {
		self.addEventListener('install', ServiceWorker.onServiceWorkerInstalled);
		self.addEventListener('activate', ServiceWorker.onServiceWorkerActivated);
		self.addEventListener('push', ServiceWorker.onPush);
		self.addEventListener('notificationclick', ServiceWorker.onNotificationClicked);
	},

	sendIF: function(url, param = null) {
		return Promise.all([DB.get("Subscribe", "subscribe"), DB.get("Options", "options"), self.registration.pushManager.getSubscription()]).then((values) => {
			var local = Object.assign(values[0], values[1]);
			var subscription = values[2];

			var form = new FormData();

			if(typeof param !== 'undefined') {
				for(var k in param) {
					form.append(k, param[k]);
				}
			}

			form.append("APP_ID", local.app_id);
			form.append("CUID", local.cuid);
			form.append("PSID", subscription.endpoint);
			form.append("DEVICE_ID", subscription.getKey('auth'));
			form.append("PNSID", "WPNS");

			return fetch(local.receiver_url + url, {
				mode: 'cors',
				method: 'POST',
				cache: 'default',
				body: form
			});
		});
	}
};

var DB = {
	database: null,
	openLock: null,
	_openDB: function(dbName) {
		return new Promise((resolve, reject) => {
			try {
				var request = indexedDB.open(dbName, 1);
			} catch(e) {
			}

			if(!request) {
				return null;
			}

			request.onsuccess = () => {
				this.database = request.result;

				resolve(this.database);
			};
		});
	},
	_ensureDBOpen: async function() {
		if(!this.openLock) {
			this.openLock = this._openDB("MPush");
		}
		await this.openLock;
		return this.database;
	},
	get: async function(table, key) {
		await this._ensureDBOpen();
		return new Promise((resolve, reject) => {
			var request = this.database.transaction(table).objectStore(table).get(key);
			request.onsuccess = () => {
				if(typeof request.result === 'undefined') {
					var error = new Error("undefined key.");
					error.name = "UndefinedKey";
					reject(error);
				} else {
					resolve(request.result);
				}
			};
			request.onerror = () => {
				reject(request.error);
			};
		});
	}
};

if (typeof self === "undefined" &&
    typeof global !== "undefined") {
  global.MPushWorker = ServiceWorker;
} else {
  self.MPushWorker = ServiceWorker;
}

console.log(typeof self);
if(typeof self !== 'undefined') {
	console.log("Morpheus PUSH ServiceWorker running.");
	MPushWorker.run();
}<commit_msg>sw updated<commit_after>var ServiceWorker = {
	onServiceWorkerInstalled: function(event) {
		event.waitUntil(self.skipWaiting());
	},

	onServiceWorkerActivated: function(event) {
    event.waitUntil(self.clients.claim());
	},

	onPush: function(event) {
		var payload = event.data.json();

		event.waitUntil(async function(){
			try {
				var json = JSON.parse(payload.aps.alert);
				var message = {};
				var title = json.title || '';

				if(json.hasOwnProperty('body')) {
					message.body = json.body;
				}
				message.data = payload;

				DB.get("Options", "options").then((local) => {
					if(json.hasOwnProperty('icon')) {
						message.icon = json.icon;
					} else if(local.hasOwnProperty("icon")) {
						message.icon = local.icon;
					}

					return self.registration.showNotification(title, message);
				});
			} catch(e) {
				DB.get("Options", "options").then((local) => {
					var message = {};
					var title = local.title || '';

					if(local.hasOwnProperty('icon')) {
						message.icon = local.icon;
					}

					message.body = payload.aps.alert;
					message.data = payload;

					return self.registration.showNotification(title, message);
				});
			}

			// Send ACK to UPMC
			return ServiceWorker.sendIF('/rcv_register_sent_ack.ctl', { SEQNO: payload.mps.seqno }).catch(function(e) { console.log(e); });
		}());
	},

	onNotificationClicked: function(event) {
		event.waitUntil(async function() {
			var option = await DB.get("Options", "options").catch(function(e) {}) || {};
			var launchUrl = option.url || self.registration.scope;
			var seqno = event.notification.data.mps.seqno;

			event.notification.close();

			if(event.notification.data) {
				try {
					var json = JSON.parse(event.notification.data.aps.alert);
					if(json.hasOwnProperty('url')) {
						launchUrl = json.url;
					}
				} catch(e) {
				}
			}

			clients.openWindow(launchUrl);

			// Send Read to UPMC
			ServiceWorker.sendIF('/rcv_register_send_result.ctl', { SEQNO: seqno }).catch(function(e) {});
		}());
	},

	run: function() {
		self.addEventListener('install', ServiceWorker.onServiceWorkerInstalled);
		self.addEventListener('activate', ServiceWorker.onServiceWorkerActivated);
		self.addEventListener('push', ServiceWorker.onPush);
		self.addEventListener('notificationclick', ServiceWorker.onNotificationClicked);
	},

	getAuthKey: async function() {
		var _self = this;
		var sendAuthKeyIF = function() {
			return new Promise(function(resolve, reject) {
				_self.sendIF("/asking_authorization.ctl").then(function(response) {
					DB.put("Subscribe", { key: "authkey", created: Math.floor(Date.now()/1000), authkey: response.BODY[0].AUTHKEY });
					resolve(response.BODY[0].AUTHKEY);
				}).catch(function(e) {
					reject(e);
				})
			});
		};

		return new Promise(function(resolve, reject) {
			DB.get("Subscribe", "authkey").then(function(result) {
				if((result.created+3600) > Math.floor(Date.now()/1000)) {
					sendAuthKeyIF().then(function(authkey) {
						resolve(authkey);
					});
				} else {
					resolve(result.authkey);
				}
			}).catch(function(e) {
				if(e.name === 'UndefinedKey') {
					sendAuthKeyIF().then(function(authkey) {
						resolve(authkey);
					});
				} else {
					reject(e);
				}
			});
		});
	},

	sendIF: async function(url, param = null) {
		var _self = this;

		return Promise.all([DB.get("Subscribe", "subscribe"), DB.get("Options", "options"), self.registration.pushManager.getSubscription()]).then((values) => {
			var local = Object.assign(values[0], values[1]);
			var subscription = values[2];

			var form = new FormData();

			if(typeof param !== 'undefined') {
				for(var k in param) {
					form.append(k, param[k]);
				}
			}

			form.append("APP_ID", local.app_id);
			form.append("CUID", local.cuid);
			form.append("PSID", subscription.endpoint);
			form.append("PNSID", "WPNS");

			return fetch(local.receiver_url + url, {
				mode: 'cors',
				method: 'POST',
				cache: 'default',
				body: form
			}).then(function(response) {
				return response.json();
			});
		});
	}
};

var DB = {
	database: null,
	openLock: null,
	_openDB: function(dbName) {
		return new Promise((resolve, reject) => {
			try {
				var request = indexedDB.open(dbName, 1);
			} catch(e) {
			}

			if(!request) {
				return null;
			}

			request.onsuccess = () => {
				this.database = request.result;

				resolve(this.database);
			};
		});
	},
	_ensureDBOpen: async function() {
		if(!this.openLock) {
			this.openLock = this._openDB("MPush");
		}
		await this.openLock;
		return this.database;
	},
	get: async function(table, key) {
		await this._ensureDBOpen();
		return new Promise((resolve, reject) => {
			var request = this.database.transaction(table).objectStore(table).get(key);
			request.onsuccess = () => {
				if(typeof request.result === 'undefined') {
					var error = new Error("undefined key.");
					error.name = "UndefinedKey";
					reject(error);
				} else {
					resolve(request.result);
				}
			};
			request.onerror = () => {
				reject(request.error);
			};
		});
	},
	put: async function(table, key) {
		await this._ensureDBOpen();
		return new Promise((resolve, reject) => {
			try {
				var request = this.database.transaction(table, 'readwrite').objectStore(table).put(key);
				request.onsuccess = () => {
					resolve(key);
				};
				request.onerror = (e) => {
					reject(e);
				};
			} catch(e) {
			}
		});
	}
};

if (typeof self === "undefined" && typeof global !== "undefined") {
  global.MPushWorker = ServiceWorker;
} else {
  self.MPushWorker = ServiceWorker;
}

if(typeof self !== 'undefined') {
	MPushWorker.run();
}<|endoftext|>


<commit_before>/*!
 * CSS Modal
 * http://drublic.github.com/css-modal
 *
 * @author Hans Christian Reinl - @drublic
 * @version 1.1.0
 */

(function (global) {

	'use strict';

	// Storage for functions and attributes
	var modal = {

		activeElement: undefined, // Store for currently active element
		lastActive: undefined, // Store for last active elemet
		stackedElements: [], // Store for stacked elements

		// All elements that can get focus, can be tabbed in a modal
		tabbableElements: 'a[href], area[href], input:not([disabled]),' +
			'select:not([disabled]), textarea:not([disabled]),' +
			'button:not([disabled]), iframe, object, embed, *[tabindex],' +
			'*[contenteditable]',

		// Polyfill addEventListener for IE8 (only very basic)
		on: function (event, element, callback) {
			if (element.addEventListener) {
				element.addEventListener(event, callback, false);
			} else {
				element.attachEvent('on' + event, callback);
			}
		},

		// Convenience function to trigger event
		trigger: function (event, modal) {
			var eventTigger;

			if (!document.createEvent) {
				return;
			}

			eventTigger = document.createEvent('Event');

			eventTigger.initEvent(event, true, true);
			eventTigger.customData = { 'modal': modal };

			document.dispatchEvent(eventTigger);
		},

		// Convenience function to add a class to an element
		addClass: function (element, className) {
			if (element && !element.className.match(/className/)) {
				element.className += ' ' + className;
			}
		},

		// Convenience function to remove a class from an element
		removeClass: function (element, className) {
			element.className = element.className.replace(' ' + className, '');
		},

		// Focus modal
		setFocus: function () {
			if (modal.activeElement) {

				// Set element with last focus
				modal.lastActive = document.activeElement;

				// New focussing
				modal.activeElement.focus();

				// Add handler to keep the focus
				modal.keepFocus(modal.activeElement);
			}
		},

		// Unfocus
		removeFocus: function () {
			if (modal.lastActive) {
				modal.lastActive.focus();
			}
		},

		// Keep focus inside the modal
		keepFocus: function (element) {
			var allTabbableElements = element.querySelectorAll(modal.tabbableElements);
			var firstTabbableElement = allTabbableElements[0];
			var lastTabbableElement = allTabbableElements[allTabbableElements.length - 1];

			var focusHandler = function (event) {
				var keyCode = event.which || event.keyCode;

				// TAB pressed
				if (keyCode === 9) {
					if (event.preventDefault) {
						event.preventDefault();
					} else {
						event.returnValue = false;
					}

					firstTabbableElement.focus();
				}
			};

			modal.on('keydown', lastTabbableElement, focusHandler);
		},

		// Mark modal as active
		setActive: function (element) {
			modal.addClass(element, 'is-active');
			modal.activeElement = element;

			// Set the focus to the modal
			modal.setFocus(element.id);

			// Fire an event
			modal.trigger('cssmodal:show', modal.activeElement);
		},

		// Unset previous active modal
		unsetActive: function (isStacked) {
			if (modal.activeElement) {
				modal.removeClass(modal.activeElement, 'is-active');

				// Fire an event
				modal.trigger('cssmodal:hide', modal.activeElement);

				// Unfocus
				modal.removeFocus();

				// Make modal stacked if needed
				if (isStacked) {
					modal.stackModal(modal.activeElement);
				}

				// If there are any stacked elements
				if (!isStacked && modal.stackedElements.length > 0) {
					modal.unstackModal();
				}

				// Reset active element
				modal.activeElement = null;
			}
		},

		// Stackable modal
		stackModal: function (stackableModal) {
			modal.addClass(stackableModal, 'is-stacked');

			// Set modal as stacked
			modal.stackedElements.push(modal.activeElement);
		},

		// Reactivate stacked modal
		unstackModal: function () {
			var stackedCount = modal.stackedElements.length;
			var lastStacked = modal.stackedElements[stackedCount - 1];

			modal.removeClass(lastStacked, 'is-stacked');

			// Set hash to modal, activates the modal automatically
			window.location.hash = lastStacked.id;

			// Remove modal from stackedElements array
			modal.stackedElements.splice(stackedCount - 1, 1);
		},

		// When showing overlay, prevent background from scrolling
		mainHandler: function () {
			var hash = window.location.hash.replace('#', '');
			var modalElement = document.getElementById(hash);
			var modalChild;

			// If the hash element exists
			if (modalElement) {

				// Get first element in selected element
				modalChild = modalElement.children[0];

				// When we deal with a modal and body-class `has-overlay` is not set
				if (modalChild && modalChild.className.match(/modal-inner/)) {

					// Set an html class to prevent scrolling
					modal.addClass(document.documentElement, 'has-overlay');

					// Make previous element stackable
					modal.unsetActive(true);

					// Mark the active element
					modal.setActive(modalElement);
				}
			} else {
				modal.removeClass(document.documentElement, 'has-overlay');

				// If activeElement is already defined, delete it
				modal.unsetActive();
			}
		}
	};

	// Hide overlay when ESC is pressed
	modal.on('keyup', document, function (event) {
		var hash = window.location.hash.replace('#', '');

		// If hash is not set
		if (hash === '' || hash === '!') {
			return;
		}

		// If key ESC is pressed
		if (event.keyCode === 27) {
			window.location.hash = '!';

			if (modal.lastActive) {
				return false;
			}

			// Unfocus
			modal.removeFocus();
		}
	}, false);

	// Trigger main handler on load and hashchange
	modal.on('hashchange', window, modal.mainHandler);
	modal.on('load', window, modal.mainHandler);

	// Export CSSModal into global space
	global.CSSModal = modal;

}(window));
<commit_msg>Add forth AND back tabbing through the modal<commit_after>/*!
 * CSS Modal
 * http://drublic.github.com/css-modal
 *
 * @author Hans Christian Reinl - @drublic
 * @version 1.1.0
 */

(function (global) {

	'use strict';

	// Storage for functions and attributes
	var modal = {

		activeElement: undefined, // Store for currently active element
		lastActive: undefined, // Store for last active elemet
		stackedElements: [], // Store for stacked elements

		// All elements that can get focus, can be tabbed in a modal
		tabbableElements: 'a[href], area[href], input:not([disabled]),' +
			'select:not([disabled]), textarea:not([disabled]),' +
			'button:not([disabled]), iframe, object, embed, *[tabindex],' +
			'*[contenteditable]',

		// Polyfill addEventListener for IE8 (only very basic)
		on: function (event, element, callback) {
			if (element.addEventListener) {
				element.addEventListener(event, callback, false);
			} else {
				element.attachEvent('on' + event, callback);
			}
		},

		// Convenience function to trigger event
		trigger: function (event, modal) {
			var eventTigger;

			if (!document.createEvent) {
				return;
			}

			eventTigger = document.createEvent('Event');

			eventTigger.initEvent(event, true, true);
			eventTigger.customData = { 'modal': modal };

			document.dispatchEvent(eventTigger);
		},

		// Convenience function to add a class to an element
		addClass: function (element, className) {
			if (element && !element.className.match(/className/)) {
				element.className += ' ' + className;
			}
		},

		// Convenience function to remove a class from an element
		removeClass: function (element, className) {
			element.className = element.className.replace(' ' + className, '');
		},

		// Focus modal
		setFocus: function () {
			if (modal.activeElement) {

				// Set element with last focus
				modal.lastActive = document.activeElement;

				// New focussing
				modal.activeElement.focus();

				// Add handler to keep the focus
				modal.keepFocus(modal.activeElement);
			}
		},

		// Unfocus
		removeFocus: function () {
			if (modal.lastActive) {
				modal.lastActive.focus();
			}
		},

		// Keep focus inside the modal
		keepFocus: function (element) {
			var allTabbableElements = element.querySelectorAll(modal.tabbableElements);
			var firstTabbableElement = allTabbableElements[0];
			var lastTabbableElement = allTabbableElements[allTabbableElements.length - 1];

			var focusHandler = function (event) {
				var keyCode = event.which || event.keyCode;

				// TAB pressed
				if (keyCode !== 9) {
					return;
				}

				// Polyfill to prevent the default behavior of events
				event.preventDefault = event.preventDefault || function () {
					event.returnValue = false;
				};

				// Move focus to first element that can be tabbed if Shift isn't used
				if (event.target === lastTabbableElement && !event.shiftKey) {
					event.preventDefault();
					firstTabbableElement.focus();

				// Move focus to last element that can be tabbed if Shift is used
				} else if (event.target === firstTabbableElement && event.shiftKey) {
					event.preventDefault();
					lastTabbableElement.focus();
				}
			};

			modal.on('keydown', element, focusHandler);
		},

		// Mark modal as active
		setActive: function (element) {
			modal.addClass(element, 'is-active');
			modal.activeElement = element;

			// Set the focus to the modal
			modal.setFocus(element.id);

			// Fire an event
			modal.trigger('cssmodal:show', modal.activeElement);
		},

		// Unset previous active modal
		unsetActive: function (isStacked) {
			if (modal.activeElement) {
				modal.removeClass(modal.activeElement, 'is-active');

				// Fire an event
				modal.trigger('cssmodal:hide', modal.activeElement);

				// Unfocus
				modal.removeFocus();

				// Make modal stacked if needed
				if (isStacked) {
					modal.stackModal(modal.activeElement);
				}

				// If there are any stacked elements
				if (!isStacked && modal.stackedElements.length > 0) {
					modal.unstackModal();
				}

				// Reset active element
				modal.activeElement = null;
			}
		},

		// Stackable modal
		stackModal: function (stackableModal) {
			modal.addClass(stackableModal, 'is-stacked');

			// Set modal as stacked
			modal.stackedElements.push(modal.activeElement);
		},

		// Reactivate stacked modal
		unstackModal: function () {
			var stackedCount = modal.stackedElements.length;
			var lastStacked = modal.stackedElements[stackedCount - 1];

			modal.removeClass(lastStacked, 'is-stacked');

			// Set hash to modal, activates the modal automatically
			window.location.hash = lastStacked.id;

			// Remove modal from stackedElements array
			modal.stackedElements.splice(stackedCount - 1, 1);
		},

		// When showing overlay, prevent background from scrolling
		mainHandler: function () {
			var hash = window.location.hash.replace('#', '');
			var modalElement = document.getElementById(hash);
			var modalChild;

			// If the hash element exists
			if (modalElement) {

				// Get first element in selected element
				modalChild = modalElement.children[0];

				// When we deal with a modal and body-class `has-overlay` is not set
				if (modalChild && modalChild.className.match(/modal-inner/)) {

					// Set an html class to prevent scrolling
					modal.addClass(document.documentElement, 'has-overlay');

					// Make previous element stackable
					modal.unsetActive(true);

					// Mark the active element
					modal.setActive(modalElement);
				}
			} else {
				modal.removeClass(document.documentElement, 'has-overlay');

				// If activeElement is already defined, delete it
				modal.unsetActive();
			}
		}
	};

	// Hide overlay when ESC is pressed
	modal.on('keyup', document, function (event) {
		var hash = window.location.hash.replace('#', '');

		// If hash is not set
		if (hash === '' || hash === '!') {
			return;
		}

		// If key ESC is pressed
		if (event.keyCode === 27) {
			window.location.hash = '!';

			if (modal.lastActive) {
				return false;
			}

			// Unfocus
			modal.removeFocus();
		}
	}, false);

	// Trigger main handler on load and hashchange
	modal.on('hashchange', window, modal.mainHandler);
	modal.on('load', window, modal.mainHandler);

	// Export CSSModal into global space
	global.CSSModal = modal;

}(window));
<|endoftext|>


<commit_before>/**
 * Base mw.TextSource object
 *
 * @param {Object} source Source object to extend
 * @param {Object} textProvider [Optional] The text provider interface ( to load source from api )
 */

( function( mw, $ ) {

	mw.TextSource = function( source ) {
		return this.init( source );
	};
	mw.TextSource.prototype = {

		//The load state:
		loaded: false,

		// Container for the captions
		// captions include "start", "end" and "content" fields
		captions: [],

		// The css style for captions ( some file formats specify display types )
		styleCss: {},

		// The previous index of the timed text served
		// Avoids searching the entire array on time updates.
		prevIndex: 0,

		/**
		 * @constructor Inherits mediaSource from embedPlayer
		 * @param {source} Base source element
		 * @param {Object} Pointer to the textProvider
		 */
		init: function( source , textProvider) {
			//	Inherits mediaSource
			for( var i in source){
				this[ i ] =  source[ i ];
			}

			// Set default category to subtitle if unset:
			if( ! this.kind ) {
				this.kind = 'subtitle';
			}
			//Set the textProvider if provided
			if( textProvider ) {
				this.textProvider = textProvider;
			}
			return this;
		},

		/**
		 * Function to load and parse the source text
		 * @param {Function} callback Function called once text source is loaded
		 */
		load: function( callback ) {
			var _this = this;
			// Setup up a callback ( in case it was not defined )
			if( !callback )
				callback = function(){ return ; };

			// Check if the captions have already been loaded:
			if( this.captions.length != 0 ){
				return callback();
			}

			// Try to load src via XHR source
			if( !this.getSrc() ) {
				mw.log( "Error: TextSource no source url for text track");
				return callback();
			}
			try {
				$.ajax({
					url: _this.getSrc(),
					success: function( data ) {
						_this.captions = _this.getCaptions( data );
						mw.log("mw.TextSource :: loaded from srt file: " + _this.captions.length + ' captions' );
						callback();
					},
					error: function( jqXHR, textStatus, errorThrown ){
						// try to load the file with the proxy:
						_this.loadViaProxy( callback );
					}
				});
			} catch ( e ){
				mw.log( "TimedText source:: first cross domain request failed, trying via proxy" );
			}
		},
		loadViaProxy: function( callback ){
			var _this = this;
			// Load via proxy:
			var proxyUrl = mw.getConfig('Mw.XmlProxyUrl');
			$.getJSON( proxyUrl + '?url=' + encodeURIComponent(  this.getSrc() ) + '&callback=?', function( result ){
				if( result['http_code'] == 'ERROR' || result['http_code'] == 0 ){
					mw.log("Error: TextSource Error with http response");
					return callback();
				}
				// Parse and load captions:
				_this.captions = _this.getCaptions( result['contents'] );
				mw.log("mw.TextSource :: loaded from proxy xml request: captions length: " + _this.captions.length + ' captions' );
				callback();
			});
		},
		/**
		* Returns the text content for requested time
		*
		* @param {Number} time Time in seconds
		*/
		getCaptionForTime: function ( time ) {
			var prevCaption = this.captions[ this.prevIndex ];
			var captionSet = {};

			// Setup the startIndex:
			if( prevCaption && time >= prevCaption.start ) {
				var startIndex = this.prevIndex;
			}else{
				// If a backwards seek start searching at the start:
				var startIndex = 0;
			}
			var firstCapIndex = 0;
			// Start looking for the text via time, add all matches that are in range
			for( var i = startIndex ; i < this.captions.length; i++ ) {
				var caption = this.captions[ i ];
				// Don't handle captions with 0 or -1 end time:
				if( caption.end == 0 || caption.end == -1)
					continue;

				if( time >= caption.start &&
					time <= caption.end ) {
					// set the earliest valid time to the current start index:
					if( !firstCapIndex ){
						firstCapIndex = caption.start;
					}

					//mw.log("Start cap time: " + caption.start + ' End time: ' + caption.end );
					captionSet[i] = caption ;
				}
				// captions are stored in start order stop search if we get larger than time
				if( caption.start > time ){
					break;
				}
			}
			// Update the prevIndex:
			this.prevIndex =firstCapIndex;
			//Return the set of captions in range:
			return captionSet;
		},
		getCaptions: function( data ){
			// Detect caption data type:
			switch( this.mimeType ){
				case 'text/x-srt':
					return this.getCaptionsFromSrt( data);
					break;
				case 'text/xml':
					return this.getCaptionsFromTMML( data );
					break;
			}
		},

		getStyleCssById: function( styleId ){
			if( this.styleCss[ styleId ] ){
				return this.styleCss[ styleId ];
			}
			return {};
		},
		/**
		 * Grab timed text from TMML format
		 *
		 * @param data
		 * @return
		 */
		getCaptionsFromTMML: function( data ){
			var _this = this;
			mw.log("TextSource::getCaptionsFromTMML");
			// set up display information:
			var captions = [];
			var xml = $.parseXML( data );

			// Check for parse error:
			if( $( xml ).find('parsererror').length ){
				mw.log("Error: close caption parse error: " +  $( xml ).find('parsererror').text() );
				return captions;
			}

			// Set the body Style
			var bodyStyleId = $( xml ).find('body').attr('style');

			// Set style translate ttml to css
			$( xml ).find( 'style').each( function( inx, style){
				var cssObject = {};
				// Map CamelCase css properties:
				$( style.attributes ).each(function(inx, attr){
					var attrName = attr.name;
					if( attrName.substr(0, 4) !== 'tts:' ){
						// skip
						return true;
					}
					var cssName = '';
					for( var c = 4; c < attrName.length; c++){
						if( attrName[c].toLowerCase() != attrName[c] ){
							cssName += '-' +  attrName[c].toLowerCase();
						} else {
							cssName+= attrName[c]
						}
					}
					cssObject[ cssName ] = attr.nodeValue;
				});
				//for(var i =0; i< style.length )
				_this.styleCss[ $( style).attr('id') ] = cssObject;
			});

			$( xml ).find( 'p' ).each( function( inx, p ){

				// Get text content ( just a quick hack, we need more detailed spec or TTML parser )
				var content = '';
				$( p.childNodes ).each(function(inx,node){
				   if( node.nodeName != '#text' && node.nodeName != 'metadata' ){
					   // Add any html tags:
					   content +='<' + node.nodeName + '/>';
				    } else {
				    	content += node.textContent;
				    }
				});

				// Get the end time:
				var end = null;
				if( $( p ).attr( 'end' ) ){
					end = mw.npt2seconds( $( p ).attr( 'end' ) );
				}
				// Look for dur
				if( !end && $( p ).attr( 'dur' )){
					end = mw.npt2seconds( $( p ).attr( 'begin' ) ) +
						mw.npt2seconds( $( p ).attr( 'dur' ) );
				}

				// Create the caption object :
				var captionObj ={
					'start': mw.npt2seconds( $( p ).attr( 'begin' ) ),
					'end': end,
					'content':  content
				};

				// See if we have custom metadata for position of this caption object
				// there are 35 columns across and 15 rows high
				var $meta = $(p).find( 'metadata' );
				if( $meta.length ){
					captionObj['css'] = {
						'position': 'absolute'
					};
					if( $meta.attr('cccol') ){
						captionObj['css']['left'] = ( $meta.attr('cccol') / 35 ) * 100 +'%';
						// also means the width has to be reduced:
						captionObj['css']['width'] =  100 - parseInt( captionObj['css']['left'] ) + '%';
					}
					if( $meta.attr('ccrow') ){
						captionObj['css']['top'] = ( $meta.attr('ccrow') / 15 ) * 100 +'%';
					}
				}
				if( $(p).attr('tts:textAlign') ){
					if( !captionObj['css'] )
						captionObj['css'] = {};
					captionObj['css']['text-align'] = $(p).attr('tts:textAlign');

					// Remove text align is "right" flip the css left:
					if( captionObj['css']['text-align'] == 'right' && captionObj['css']['left'] ){
						captionObj['css']['width'] = captionObj['css']['left'];
						captionObj['css']['left'] = null;
					}
				}

				// check if this p has any style else use the body parent
				if( $(p).attr('style') ){
					captionObj['styleId'] = $(p).attr('style') ;
				} else {
					captionObj['styleId'] = bodyStyleId;
				}
				captions.push( captionObj);
			});
			return captions;
		},
		/**
		 * srt timed text parse handle:
		 * @param {String} data Srt string to be parsed
		 */
		getCaptionsFromSrt: function ( data ){
			mw.log("TextSource::getCaptionsFromSrt");
			// check if the "srt" parses as an XML
			try{
				var xml = $.parseXML( data );
				if( xml && $( xml ).find('parsererror').length == 0 ){
					return this.getCaptionsFromTMML( data );
				}
			} catch ( e ){
				// srt should not be xml
			}
			// Remove dos newlines
			var srt = data.replace(/\r+/g, '');

			// Trim white space start and end
			srt = srt.replace(/^\s+|\s+$/g, '');

			// Remove all html tags for security reasons
			srt = srt.replace(/<[a-zA-Z\/][^>]*>/g, '');

			// Get captions
			var captions = [];
			var caplist = srt.split('\n\n');
			for (var i = 0; i < caplist.length; i++) {
		 		var caption = "";
				var content, start, end, s;
				caption = caplist[i];
				s = caption.split(/\n/);
				if (s.length < 2) {
					// file format error or comment lines
					continue;
				}
				if (s[0].match(/^\d+$/) && s[1].match(/\d+:\d+:\d+/)) {
					// ignore caption number in s[0]
					// parse time string
					var m = s[1].match(/(\d+):(\d+):(\d+)(?:,(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:,(\d+))?/);
					if (m) {
						var startms = ( m[4] )? (parseInt(m[4], 10) / 1000) : 0;
						start =
							(parseInt(m[1], 10) * 60 * 60) +
							(parseInt(m[2], 10) * 60) +
							(parseInt(m[3], 10)) +
							startms;

						var endms = ( m[8] )? (parseInt(m[8], 10) / 1000) : 0;
						end =
							(parseInt(m[5], 10) * 60 * 60) +
							(parseInt(m[6], 10) * 60) +
							(parseInt(m[7], 10)) +
							endms;
					} else {
						// Unrecognized timestring
						continue;
					}
					// concatenate text lines to html text
					content = s.slice(2).join("<br>");
				} else {
					// file format error or comment lines
					continue;
				}
				captions.push({
					'start' : start,
					'end' : end,
					'content' : content
				} );
			}

			return captions;
		}
	};

} )( window.mediaWiki, window.jQuery );<commit_msg>make sure the xml is valid before checking for error <commit_after>/**
 * Base mw.TextSource object
 *
 * @param {Object} source Source object to extend
 * @param {Object} textProvider [Optional] The text provider interface ( to load source from api )
 */

( function( mw, $ ) {

	mw.TextSource = function( source ) {
		return this.init( source );
	};
	mw.TextSource.prototype = {

		//The load state:
		loaded: false,

		// Container for the captions
		// captions include "start", "end" and "content" fields
		captions: [],

		// The css style for captions ( some file formats specify display types )
		styleCss: {},

		// The previous index of the timed text served
		// Avoids searching the entire array on time updates.
		prevIndex: 0,

		/**
		 * @constructor Inherits mediaSource from embedPlayer
		 * @param {source} Base source element
		 * @param {Object} Pointer to the textProvider
		 */
		init: function( source , textProvider) {
			//	Inherits mediaSource
			for( var i in source){
				this[ i ] =  source[ i ];
			}

			// Set default category to subtitle if unset:
			if( ! this.kind ) {
				this.kind = 'subtitle';
			}
			//Set the textProvider if provided
			if( textProvider ) {
				this.textProvider = textProvider;
			}
			return this;
		},

		/**
		 * Function to load and parse the source text
		 * @param {Function} callback Function called once text source is loaded
		 */
		load: function( callback ) {
			var _this = this;
			// Setup up a callback ( in case it was not defined )
			if( !callback )
				callback = function(){ return ; };

			// Check if the captions have already been loaded:
			if( this.captions.length != 0 ){
				return callback();
			}

			// Try to load src via XHR source
			if( !this.getSrc() ) {
				mw.log( "Error: TextSource no source url for text track");
				return callback();
			}
			try {
				$.ajax({
					url: _this.getSrc(),
					success: function( data ) {
						_this.captions = _this.getCaptions( data );
						mw.log("mw.TextSource :: loaded from srt file: " + _this.captions.length + ' captions' );
						callback();
					},
					error: function( jqXHR, textStatus, errorThrown ){
						// try to load the file with the proxy:
						_this.loadViaProxy( callback );
					}
				});
			} catch ( e ){
				mw.log( "TimedText source:: first cross domain request failed, trying via proxy" );
			}
		},
		loadViaProxy: function( callback ){
			var _this = this;
			// Load via proxy:
			var proxyUrl = mw.getConfig('Mw.XmlProxyUrl');
			$.getJSON( proxyUrl + '?url=' + encodeURIComponent(  this.getSrc() ) + '&callback=?', function( result ){
				if( result['http_code'] == 'ERROR' || result['http_code'] == 0 ){
					mw.log("Error: TextSource Error with http response");
					return callback();
				}
				// Parse and load captions:
				_this.captions = _this.getCaptions( result['contents'] );
				mw.log("mw.TextSource :: loaded from proxy xml request: captions length: " + _this.captions.length + ' captions' );
				callback();
			});
		},
		/**
		* Returns the text content for requested time
		*
		* @param {Number} time Time in seconds
		*/
		getCaptionForTime: function ( time ) {
			var prevCaption = this.captions[ this.prevIndex ];
			var captionSet = {};

			// Setup the startIndex:
			if( prevCaption && time >= prevCaption.start ) {
				var startIndex = this.prevIndex;
			}else{
				// If a backwards seek start searching at the start:
				var startIndex = 0;
			}
			var firstCapIndex = 0;
			// Start looking for the text via time, add all matches that are in range
			for( var i = startIndex ; i < this.captions.length; i++ ) {
				var caption = this.captions[ i ];
				// Don't handle captions with 0 or -1 end time:
				if( caption.end == 0 || caption.end == -1)
					continue;

				if( time >= caption.start &&
					time <= caption.end ) {
					// set the earliest valid time to the current start index:
					if( !firstCapIndex ){
						firstCapIndex = caption.start;
					}

					//mw.log("Start cap time: " + caption.start + ' End time: ' + caption.end );
					captionSet[i] = caption ;
				}
				// captions are stored in start order stop search if we get larger than time
				if( caption.start > time ){
					break;
				}
			}
			// Update the prevIndex:
			this.prevIndex =firstCapIndex;
			//Return the set of captions in range:
			return captionSet;
		},
		getCaptions: function( data ){
			// Detect caption data type:
			switch( this.mimeType ){
				case 'text/x-srt':
					return this.getCaptionsFromSrt( data);
					break;
				case 'text/xml':
					return this.getCaptionsFromTMML( data );
					break;
			}
		},

		getStyleCssById: function( styleId ){
			if( this.styleCss[ styleId ] ){
				return this.styleCss[ styleId ];
			}
			return {};
		},
		/**
		 * Grab timed text from TMML format
		 *
		 * @param data
		 * @return
		 */
		getCaptionsFromTMML: function( data ){
			var _this = this;
			mw.log("TextSource::getCaptionsFromTMML");
			// set up display information:
			var captions = [];
			var xml = $.parseXML( data );

			// Check for parse error:
			if( !xml || $( xml ).find('parsererror').length ){
				mw.log("Error: close caption parse error: " +  $( xml ).find('parsererror').text() );
				return captions;
			}

			// Set the body Style
			var bodyStyleId = $( xml ).find('body').attr('style');

			// Set style translate ttml to css
			$( xml ).find( 'style').each( function( inx, style){
				var cssObject = {};
				// Map CamelCase css properties:
				$( style.attributes ).each(function(inx, attr){
					var attrName = attr.name;
					if( attrName.substr(0, 4) !== 'tts:' ){
						// skip
						return true;
					}
					var cssName = '';
					for( var c = 4; c < attrName.length; c++){
						if( attrName[c].toLowerCase() != attrName[c] ){
							cssName += '-' +  attrName[c].toLowerCase();
						} else {
							cssName+= attrName[c]
						}
					}
					cssObject[ cssName ] = attr.nodeValue;
				});
				//for(var i =0; i< style.length )
				_this.styleCss[ $( style).attr('id') ] = cssObject;
			});

			$( xml ).find( 'p' ).each( function( inx, p ){

				// Get text content ( just a quick hack, we need more detailed spec or TTML parser )
				var content = '';
				$( p.childNodes ).each(function(inx,node){
				   if( node.nodeName != '#text' && node.nodeName != 'metadata' ){
					   // Add any html tags:
					   content +='<' + node.nodeName + '/>';
				    } else {
				    	content += node.textContent;
				    }
				});

				// Get the end time:
				var end = null;
				if( $( p ).attr( 'end' ) ){
					end = mw.npt2seconds( $( p ).attr( 'end' ) );
				}
				// Look for dur
				if( !end && $( p ).attr( 'dur' )){
					end = mw.npt2seconds( $( p ).attr( 'begin' ) ) +
						mw.npt2seconds( $( p ).attr( 'dur' ) );
				}

				// Create the caption object :
				var captionObj ={
					'start': mw.npt2seconds( $( p ).attr( 'begin' ) ),
					'end': end,
					'content':  content
				};

				// See if we have custom metadata for position of this caption object
				// there are 35 columns across and 15 rows high
				var $meta = $(p).find( 'metadata' );
				if( $meta.length ){
					captionObj['css'] = {
						'position': 'absolute'
					};
					if( $meta.attr('cccol') ){
						captionObj['css']['left'] = ( $meta.attr('cccol') / 35 ) * 100 +'%';
						// also means the width has to be reduced:
						captionObj['css']['width'] =  100 - parseInt( captionObj['css']['left'] ) + '%';
					}
					if( $meta.attr('ccrow') ){
						captionObj['css']['top'] = ( $meta.attr('ccrow') / 15 ) * 100 +'%';
					}
				}
				if( $(p).attr('tts:textAlign') ){
					if( !captionObj['css'] )
						captionObj['css'] = {};
					captionObj['css']['text-align'] = $(p).attr('tts:textAlign');

					// Remove text align is "right" flip the css left:
					if( captionObj['css']['text-align'] == 'right' && captionObj['css']['left'] ){
						captionObj['css']['width'] = captionObj['css']['left'];
						captionObj['css']['left'] = null;
					}
				}

				// check if this p has any style else use the body parent
				if( $(p).attr('style') ){
					captionObj['styleId'] = $(p).attr('style') ;
				} else {
					captionObj['styleId'] = bodyStyleId;
				}
				captions.push( captionObj);
			});
			return captions;
		},
		/**
		 * srt timed text parse handle:
		 * @param {String} data Srt string to be parsed
		 */
		getCaptionsFromSrt: function ( data ){
			mw.log("TextSource::getCaptionsFromSrt");
			// check if the "srt" parses as an XML
			try{
				var xml = $.parseXML( data );
				if( xml && $( xml ).find('parsererror').length == 0 ){
					return this.getCaptionsFromTMML( data );
				}
			} catch ( e ){
				// srt should not be xml
			}
			// Remove dos newlines
			var srt = data.replace(/\r+/g, '');

			// Trim white space start and end
			srt = srt.replace(/^\s+|\s+$/g, '');

			// Remove all html tags for security reasons
			srt = srt.replace(/<[a-zA-Z\/][^>]*>/g, '');

			// Get captions
			var captions = [];
			var caplist = srt.split('\n\n');
			for (var i = 0; i < caplist.length; i++) {
		 		var caption = "";
				var content, start, end, s;
				caption = caplist[i];
				s = caption.split(/\n/);
				if (s.length < 2) {
					// file format error or comment lines
					continue;
				}
				if (s[0].match(/^\d+$/) && s[1].match(/\d+:\d+:\d+/)) {
					// ignore caption number in s[0]
					// parse time string
					var m = s[1].match(/(\d+):(\d+):(\d+)(?:,(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:,(\d+))?/);
					if (m) {
						var startms = ( m[4] )? (parseInt(m[4], 10) / 1000) : 0;
						start =
							(parseInt(m[1], 10) * 60 * 60) +
							(parseInt(m[2], 10) * 60) +
							(parseInt(m[3], 10)) +
							startms;

						var endms = ( m[8] )? (parseInt(m[8], 10) / 1000) : 0;
						end =
							(parseInt(m[5], 10) * 60 * 60) +
							(parseInt(m[6], 10) * 60) +
							(parseInt(m[7], 10)) +
							endms;
					} else {
						// Unrecognized timestring
						continue;
					}
					// concatenate text lines to html text
					content = s.slice(2).join("<br>");
				} else {
					// file format error or comment lines
					continue;
				}
				captions.push({
					'start' : start,
					'end' : end,
					'content' : content
				} );
			}

			return captions;
		}
	};

} )( window.mediaWiki, window.jQuery );<|endoftext|>


<commit_before>casper.on("remote.message", function(msg) {
    this.echo("Console: " + msg);
});

casper.on("page.error", function(msg, trace) {
    this.echo("page Error: " + msg);
});

casper.on("resource.error", function(msg, trace) {
    this.echo("Res.Error: " + msg);
});

function getInfo(elemName) {
    return casper.page.evaluate(function(id) {
        var theElement = document.getElementById(id);
        var rect = theElement.getBoundingClientRect();
        return {
            value: 'getTypedValue' in theElement?theElement.getTypedValue():'not adapted',
            cx: Math.floor((rect.left+rect.right)/2),
            cy: Math.floor((rect.top+rect.bottom)/2),
            isActive: document.activeElement === theElement
        }
    }, elemName);
}
function sendEvent(event, elem) {
    casper.page.sendEvent(event, elem.cx, elem.cy);
}

function sendKey(key){
    with(casper.page) {
        sendEvent('keydown', key);
        sendEvent('keyup', key);
    }
}

casper.test.begin("Test checkbox", function(test) {
    casper.start('http://localhost:43091/demo', function() {
        test.assertTitle('tedede demo', 'titulo correcto');
        test.assertExists('#bool1', 'tengo bool1');
        var boolN = 'bool1';
        var bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "default value is false");
        sendEvent('click', bool1);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === true, "one click sets it to true");
        sendKey(casper.page.event.key.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false");
        sendKey(casper.page.event.key.Delete);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "delete sets null");
        sendKey(casper.page.event.key.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false (2)");
        sendKey(casper.page.event.key.Backspace);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "backspace sets null");
        sendKey(casper.page.event.key.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false (3)");
        sendKey(casper.page.event.key.Minus);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "minus sets null");
        sendKey(casper.page.event.key.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false (4)");
        sendKey(casper.page.event.key.Period);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "period sets null");
    }).run(function() {
        test.done();
    });
});
<commit_msg>CasperJS: acorto la definición de las keys<commit_after>casper.on("remote.message", function(msg) {
    this.echo("Console: " + msg);
});

casper.on("page.error", function(msg, trace) {
    this.echo("page Error: " + msg);
});

casper.on("resource.error", function(msg, trace) {
    this.echo("Res.Error: " + msg);
});

function getInfo(elemName) {
    return casper.page.evaluate(function(id) {
        var theElement = document.getElementById(id);
        var rect = theElement.getBoundingClientRect();
        return {
            value: 'getTypedValue' in theElement?theElement.getTypedValue():'not adapted',
            cx: Math.floor((rect.left+rect.right)/2),
            cy: Math.floor((rect.top+rect.bottom)/2),
            isActive: document.activeElement === theElement
        }
    }, elemName);
}
function sendEvent(event, elem) {
    casper.page.sendEvent(event, elem.cx, elem.cy);
}

function sendKey(key){
    with(casper.page) {
        sendEvent('keydown', key);
        sendEvent('keyup', key);
    }
}

var keys = null;

casper.test.begin("Test checkbox", function(test) {
    casper.start('http://localhost:43091/demo', function() {
        keys = casper.page.event.key;
        test.assertTitle('tedede demo', 'titulo correcto');
        test.assertExists('#bool1', 'tengo bool1');
        var boolN = 'bool1';
        var bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "default value is false");
        sendEvent('click', bool1);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === true, "one click sets it to true");
        sendKey(keys.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false");
        sendKey(keys.Delete);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "delete sets null");
        sendKey(keys.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false (2)");
        sendKey(keys.Backspace);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "backspace sets null");
        sendKey(keys.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false (3)");
        sendKey(keys.Minus);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "minus sets null");
        sendKey(keys.Space);
        bool1 = getInfo(boolN);
        test.assert(bool1.value === false, "space sets to false (4)");
        sendKey(keys.Period);
        bool1 = getInfo(boolN);
        test.assert(! bool1.value, "period sets null");
    }).run(function() {
        test.done();
    });
});
<|endoftext|>


<commit_before>/**
 * @fileverview 前端模板文件
 * @author xukai.ken@gmail.com
 */

define(function(require, exports, module) {

	// 评论模板
	exports.commentTPL = '
\

\
	    						\

\

\

#{author} 说：
\

#{month} #{date}, #{year} at #{hour}:#{minute}
\

\

\

#{comment}
\

';

	  exports.dialogTPL = '';

});<commit_msg>Change the template structure.<commit_after>/**
 * @fileverview 前端模板文件
 * @author xukai.ken@gmail.com
 */

define(function(require, exports, module) {

	// 评论模板
	exports.commentTPL = '
\

\
			    				回复\

\

\

\

#{author} 说：
\

#{month} #{date}, #{year} at #{hour}:#{minute}
\

\

\

#{comment}
\

';

	  exports.dialogTPL = '';
});<|endoftext|>


<commit_before>// Future versions of Hyper may add additional config options,
// which will not automatically be merged into this file.
// See https://hyper.is#cfg for all currently supported options.

module.exports = {
  config: {
    // Choose either "stable" for receiving highly polished,
    // or "canary" for less polished but more frequent updates
    updateChannel: 'stable',

    // default font size in pixels for all tabs
    fontSize: 16,

    // font family with optional fallbacks
    fontFamily: '"SF Mono", "Anka/Coder", Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace',

    // terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk)
    cursorColor: 'rgba(248,28,229,0.8)',

    // `BEAM` for |, `UNDERLINE` for _, `BLOCK` for █
    cursorShape: 'BLOCK',

    // set to true for blinking cursor
    cursorBlink: false,

    // color of the text
    foregroundColor: '#fff',

    // terminal background color
    backgroundColor: '#000',

    // border color (window, tabs)
    borderColor: '#333',

    // custom css to embed in the main window
    css: '',

    // custom css to embed in the terminal window
    termCSS: '',

    // set to `true` (without backticks) if you're using a Linux setup that doesn't show native menus
    // default: `false` on Linux, `true` on Windows (ignored on macOS)
    showHamburgerMenu: '',

    // set to `false` if you want to hide the minimize, maximize and close buttons
    // additionally, set to `'left'` if you want them on the left, like in Ubuntu
    // default: `true` on windows and Linux (ignored on macOS)
    showWindowControls: '',

    // custom padding (css format, i.e.: `top right bottom left`)
    padding: '12px 14px',

    // the full list. if you're going to provide the full color palette,
    // including the 6 x 6 color cubes and the grayscale map, just provide
    // an array here instead of a color map object
    colors: {
      black: '#000000',
      red: '#ff0000',
      green: '#33ff00',
      yellow: '#ffff00',
      blue: '#0066ff',
      magenta: '#cc00ff',
      cyan: '#00ffff',
      white: '#d0d0d0',
      lightBlack: '#808080',
      lightRed: '#ff0000',
      lightGreen: '#33ff00',
      lightYellow: '#ffff00',
      lightBlue: '#0066ff',
      lightMagenta: '#cc00ff',
      lightCyan: '#00ffff',
      lightWhite: '#ffffff'
    },

    // the shell to run when spawning a new session (i.e. /usr/local/bin/fish)
    // if left empty, your system's login shell will be used by default
    //
    // Windows
    // - Make sure to use a full path if the binary name doesn't work
    // - Remove `--login` in shellArgs
    //
    // Bash on Windows
    // - Example: `C:\\Windows\\System32\\bash.exe`
    //
    // Powershell on Windows
    // - Example: `C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
    shell: '',

    // for setting shell arguments (i.e. for using interactive shellArgs: ['-i'])
    // by default ['--login'] will be used
    shellArgs: ['--login'],

    // for environment variables
    env: {},

    // set to false for no bell
    bell: 'SOUND',

    // if true, selected text will automatically be copied to the clipboard
    copyOnSelect: false

    // if true, on right click selected text will be copied or pasted if no
    // selection is present (true by default on Windows)
    // quickEdit: true

    // URL to custom bell
    // bellSoundURL: 'http://example.com/bell.mp3',

    // for advanced config flags please refer to https://hyper.is/#cfg
  },

  // a list of plugins to fetch and install from npm
  // format: [@org/]project[#version]
  // examples:
  //   `hyperpower`
  //   `@company/project`
  //   `project#1.0.1`
  plugins: ["hyperterm-adventurous", "hyperlinks"],

  // in development, you can create a directory under
  // `~/.hyper_plugins/local/` and include it here
  // to load it and avoid it being `npm install`ed
  localPlugins: [],

  keymaps: {
    // Example
    // 'window:devtools': 'cmd+alt+o',
  }
};
<commit_msg>Grow the font size of tab titles in Hyper a bit<commit_after>// Future versions of Hyper may add additional config options,
// which will not automatically be merged into this file.
// See https://hyper.is#cfg for all currently supported options.

module.exports = {
  config: {
    // Choose either "stable" for receiving highly polished,
    // or "canary" for less polished but more frequent updates
    updateChannel: 'stable',

    // default font size in pixels for all tabs
    fontSize: 16,

    // font family with optional fallbacks
    fontFamily: '"SF Mono", "Anka/Coder", Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace',

    // terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk)
    cursorColor: 'rgba(248,28,229,0.8)',

    // `BEAM` for |, `UNDERLINE` for _, `BLOCK` for █
    cursorShape: 'BLOCK',

    // set to true for blinking cursor
    cursorBlink: false,

    // color of the text
    foregroundColor: '#fff',

    // terminal background color
    backgroundColor: '#000',

    // border color (window, tabs)
    borderColor: '#333',

    // custom css to embed in the main window
    css: '.tabs_title { font-size: 14px; }',

    // custom css to embed in the terminal window
    termCSS: '',

    // set to `true` (without backticks) if you're using a Linux setup that doesn't show native menus
    // default: `false` on Linux, `true` on Windows (ignored on macOS)
    showHamburgerMenu: '',

    // set to `false` if you want to hide the minimize, maximize and close buttons
    // additionally, set to `'left'` if you want them on the left, like in Ubuntu
    // default: `true` on windows and Linux (ignored on macOS)
    showWindowControls: '',

    // custom padding (css format, i.e.: `top right bottom left`)
    padding: '12px 14px',

    // the full list. if you're going to provide the full color palette,
    // including the 6 x 6 color cubes and the grayscale map, just provide
    // an array here instead of a color map object
    colors: {
      black: '#000000',
      red: '#ff0000',
      green: '#33ff00',
      yellow: '#ffff00',
      blue: '#0066ff',
      magenta: '#cc00ff',
      cyan: '#00ffff',
      white: '#d0d0d0',
      lightBlack: '#808080',
      lightRed: '#ff0000',
      lightGreen: '#33ff00',
      lightYellow: '#ffff00',
      lightBlue: '#0066ff',
      lightMagenta: '#cc00ff',
      lightCyan: '#00ffff',
      lightWhite: '#ffffff'
    },

    // the shell to run when spawning a new session (i.e. /usr/local/bin/fish)
    // if left empty, your system's login shell will be used by default
    //
    // Windows
    // - Make sure to use a full path if the binary name doesn't work
    // - Remove `--login` in shellArgs
    //
    // Bash on Windows
    // - Example: `C:\\Windows\\System32\\bash.exe`
    //
    // Powershell on Windows
    // - Example: `C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
    shell: '',

    // for setting shell arguments (i.e. for using interactive shellArgs: ['-i'])
    // by default ['--login'] will be used
    shellArgs: ['--login'],

    // for environment variables
    env: {},

    // set to false for no bell
    bell: 'SOUND',

    // if true, selected text will automatically be copied to the clipboard
    copyOnSelect: false

    // if true, on right click selected text will be copied or pasted if no
    // selection is present (true by default on Windows)
    // quickEdit: true

    // URL to custom bell
    // bellSoundURL: 'http://example.com/bell.mp3',

    // for advanced config flags please refer to https://hyper.is/#cfg
  },

  // a list of plugins to fetch and install from npm
  // format: [@org/]project[#version]
  // examples:
  //   `hyperpower`
  //   `@company/project`
  //   `project#1.0.1`
  plugins: ["hyperterm-adventurous", "hyperlinks"],

  // in development, you can create a directory under
  // `~/.hyper_plugins/local/` and include it here
  // to load it and avoid it being `npm install`ed
  localPlugins: [],

  keymaps: {
    // Example
    // 'window:devtools': 'cmd+alt+o',
  }
};
<|endoftext|>