<commit_before>import log from '../../utils/log';
import Utils from '../../utils/utils';
import Texture from '../../gl/texture';

import FontFaceObserver from 'fontfaceobserver';

export default class CanvasText {

    constructor () {
        this.canvas = document.createElement('canvas');
        this.canvas.style.backgroundColor = 'transparent'; // render text on transparent background
        this.context = this.canvas.getContext('2d');
    }

    resize (width, height) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.context.clearRect(0, 0, width, height);
    }

    // Set font style params for canvas drawing
    setFont ({ font_css, fill, stroke, stroke_width, px_size }) {
        this.px_size = px_size;
        this.text_buffer = 8; // pixel padding around text
        let ctx = this.context;

        ctx.font = font_css;
        if (stroke) {
            ctx.strokeStyle = stroke;
            ctx.lineWidth = stroke_width;
        }
        else {
            ctx.strokeStyle = null;
            ctx.lineWidth = 0;
        }
        ctx.fillStyle = fill;
        ctx.miterLimit = 2;
    }

    textSizes (texts) {
        return CanvasText.fonts_loaded.then(() => {
            for (let style in texts) {
                let text_infos = texts[style];
                let first = true;

                for (let text in text_infos) {
                    // Use cached size, or compute via canvas
                    if (!CanvasText.text_cache[style] || !CanvasText.text_cache[style][text]) {
                        let text_settings = text_infos[text].text_settings;
                        if (first) {
                            this.setFont(text_settings);
                            first = false;
                        }

                        CanvasText.text_cache[style] = CanvasText.text_cache[style] || {};
                        CanvasText.text_cache[style][text] =
                            this.textSize(text, text_settings.transform, text_settings.text_wrap);
                        CanvasText.cache_stats.misses++;
                    }
                    else {
                        CanvasText.cache_stats.hits++;
                    }

                    // Only send text sizes back to worker (keep computed text line info
                    // on main thread, for future rendering)
                    text_infos[text].size = CanvasText.text_cache[style][text].size;
                }
            }

            return texts;
        });
    }

    // Computes width and height of text based on current font style
    // Includes word wrapping, returns size info for whole text block and individual lines
    textSize (text, transform, text_wrap) {
        let str = this.applyTextTransform(text, transform);
        let ctx = this.context;
        let buffer = this.text_buffer * Utils.device_pixel_ratio;
        let leading = 2 * Utils.device_pixel_ratio; // make configurable and/or use Canvas TextMetrics when available
        let line_height = this.px_size + leading; // px_size already in device pixels

        // Word wrapping
        // Line breaks can be caused by:
        //  - implicit line break when a maximum character threshold is exceeded per line (text_wrap)
        //  - explicit line break in the label text (\n)
        let words;
        if (typeof text_wrap === 'number') {
            words = str.split(' '); // split words on spaces
        }
        else {
            words = [str]; // no max line word wrapping (but new lines will still be in effect)
        }
        let new_line_template = { width: 0, chars: 0, text: '' };
        let line = Object.assign({}, new_line_template); // current line
        let lines = []; // completed lines
        let max_width = 0; // max width to fit all lines

        // add current line buffer to completed lines, optionally start new line
        function addLine (new_line) {
            line.text = line.text.trim();
            if (line.text.length > 0) {
                line.width = ctx.measureText(line.text).width;
                max_width = Math.max(max_width, Math.ceil(line.width));
                lines.push(line);
            }
            if (new_line) {
                line = Object.assign({}, new_line_template);
            }
        }

        // First iterate on space-break groups (will be one if max line length off), then iterate on line-break groups
        for (let w=0; w < words.length; w++) {
            let breaks = words[w].split('\n'); // split on line breaks

            for (let n=0; n < breaks.length; n++) {
                let word = breaks[n];

                // if adding current word would overflow, add a new line instead
                if (line.chars + word.length > text_wrap && line.chars > 0) {
                    addLine(true);
                }

                // add current word (plus space)
                line.chars += word.length + 1;
                line.text += word + ' ';

                // if line breaks present, add new line (unless on last line)
                if (breaks.length > 1 && n < breaks.length - 1) {
                    addLine(true);
                }
            }
        }
        addLine(false);

        // Final dimensions of text
        let height = lines.length * line_height;

        let collision_size = [
            max_width / Utils.device_pixel_ratio,
            height / Utils.device_pixel_ratio
        ];

        let texture_size = [
            max_width + buffer * 2,
            height + buffer * 2
        ];

        let logical_size = texture_size.map(v => v / Utils.device_pixel_ratio);

        // Returns lines (w/per-line info for drawing) and text's overall bounding box + canvas size
        return {
            lines,
            size: { collision_size, texture_size, logical_size, line_height }
        };
    }

    // Draw one or more lines of text at specified location, adjusting for buffer and baseline
    drawText (lines, [x, y], size, { stroke, transform, align }) {
        align = align || 'center';

        for (let line_num=0; line_num < lines.length; line_num++) {
            let line = lines[line_num];
            let str = this.applyTextTransform(line.text, transform);
            let buffer = this.text_buffer * Utils.device_pixel_ratio;
            let texture_size = size.texture_size;
            let line_height = size.line_height;

            // Text alignment
            let tx;
            if (align === 'left') {
                tx = x + buffer;
            }
            else if (align === 'center') {
                tx = x + texture_size[0]/2 - line.width/2;
            }
            else if (align === 'right') {
                tx = x + texture_size[0] - line.width - buffer;
            }

            // In the absence of better Canvas TextMetrics (not supported by browsers yet),
            // 0.75 buffer produces a better approximate vertical centering of text
            let ty = y + buffer * 0.75 + (line_num + 1) * line_height;

            if (stroke) {
                this.context.strokeText(str, tx, ty);
            }
            this.context.fillText(str, tx, ty);
        }
    }

    rasterize (texts, texture_size) {
        for (let style in texts) {
            let text_infos = texts[style];
            let first = true;

            for (let text in text_infos) {
                let info = text_infos[text];
                let text_settings = info.text_settings;
                let lines = CanvasText.text_cache[style][text].lines; // get previously computed lines of text

                if (first) {
                    this.setFont(text_settings);
                    first = false;
                }

                this.drawText(lines, info.position, info.size, {
                    stroke: text_settings.stroke,
                    transform: text_settings.transform,
                    align: text_settings.align
                });

                info.texcoords = Texture.getTexcoordsForSprite(
                    info.position,
                    info.size.texture_size,
                    texture_size
                );
            }
        }
    }

    // Place text labels within an atlas of the given max size
    setTextureTextPositions (texts, max_texture_size) {
        // Find widest label
        let widest = 0;
        for (let style in texts) {
            let text_infos = texts[style];
            for (let text in text_infos) {
                let size = text_infos[text].size.texture_size;
                if (size[0] > widest) {
                    widest = size[0];
                }
            }
        }

        // Layout labels, stacked in columns
        let cx = 0, cy = 0; // current x/y position in atlas
        let height = 0;     // overall atlas height
        for (let style in texts) {
            let text_infos = texts[style];
            for (let text in text_infos) {
                let text_info = text_infos[text];
                let size = text_info.size.texture_size;
                if (cy + size[1] < max_texture_size) {
                    text_info.position = [cx, cy]; // add label to current column
                    cy += size[1];
                    if (cy > height) {
                        height = cy;
                    }
                }
                else { // start new column if taller than texture
                    cx += widest;
                    cy = 0;
                    text_info.position = [cx, cy];
                }
            }
        }

        return [cx + widest, height]; // overall atlas size
    }

    // Called before rasterization
    applyTextTransform (text, transform) {
        if (transform === 'capitalize') {
            return text.replace(/\w\S*/g, function (txt) {
                return txt.charAt(0).toUpperCase() + txt.substr(1);
            });
        }
        else if (transform === 'uppercase') {
            return text.toUpperCase();
        }
        else if (transform === 'lowercase') {
            return text.toLowerCase();
        }
        return text;
    }

    // Convert font CSS-style size ('12px', '14pt', '1.5em', etc.) to pixel size (adjusted for device pixel ratio)
    // Defaults units to pixels if not specified
    static fontPixelSize (size) {
        if (size == null) {
            return;
        }
        size = (typeof size === 'string') ? size : String(size); // need a string for regex

        let [, px_size, units] = size.match(CanvasText.font_size_re) || [];
        units = units || 'px';

        if (units === "em") {
            px_size *= 16;
        } else if (units === "pt") {
            px_size /= 0.75;
        } else if (units === "%") {
            px_size /= 6.25;
        }

        px_size = parseFloat(px_size);
        px_size *= Utils.device_pixel_ratio;
        return px_size;
    }

    // Load set of custom font faces
    // `fonts` is an object where the key is a font family name, and the value is one or more font face
    // definitions. The value can be either a single object, or an array of such objects.
    // If the special string value 'external' is used, it indicates the the font will be loaded via external CSS.
    static loadFonts (fonts) {
        let queue = [];
        for (let family in fonts) {
            if (Array.isArray(fonts[family])) {
                fonts[family].forEach(face => queue.push(this.loadFontFace(family, face)));
            }
            else {
                queue.push(this.loadFontFace(family, fonts[family]));
            }
        }

        CanvasText.fonts_loaded = Promise.all(queue.filter(x => x));
        return CanvasText.fonts_loaded;
    }

    // Load a single font face
    // `face` contains the font face definition, with optional parameters for `weight`, `style`, and `url`.
    // If the `url` is defined, the font is injected into the document as a CSS font-face.
    // If the object's value is the special string 'external', or if no `url` is defined, then the font face
    // is assumed is assumed to been loaded via external CSS. In either case, the function returns a promise
    // that resolves when the font face has loaded, or times out.
    static loadFontFace (family, face) {
        if (face == null || (typeof face !== 'object' && face !== 'external')) {
            return;
        }

        let options = { family };

        if (typeof face === 'object') {
            Object.assign(options, face);

            // If URL is defined, inject font into document
            if (typeof face.url === 'string') {
                this.injectFontFace(options);
            }
        }

        // Wait for font to load
        return (new FontFaceObserver(family, options)).load().then(
            () => {
                // Promise resolves, font is available
                log('debug', `Font face '${family}' is available`, options);
            },
            () => {
                // Promise rejects, font is not available
                log('debug', `Font face '${family}' is NOT available`, options);
            }
        );
    }

    // Loads a font face via CSS injection
    // TODO: consider support for multiple format URLs per face, unicode ranges
    static injectFontFace ({ family, url, weight, style }) {
        let css = `
            @font-face {
                font-family: '${family}';
                font-weight: ${weight || 'normal'};
                font-style: ${style || 'normal'};
                src: url(${encodeURI(url)});
            }
        `;

        let style_el = document.createElement('style');
        style_el.appendChild(document.createTextNode(""));
        document.head.appendChild(style_el);
        style_el.sheet.insertRule(css, 0);
        log('trace', 'Injecting CSS font face:', css);
    }

}

// Extract font size and units
CanvasText.font_size_re = /((?:[0-9]*\.)?[0-9]+)\s*(px|pt|em|%)?/;

// Cache sizes of rendered text
CanvasText.text_cache = {}; // by text style, then text string
CanvasText.cache_stats = { hits: 0, misses: 0 };

// Font detection
CanvasText.fonts_loaded = Promise.resolve(); // resolves when all requested fonts have been detected
<commit_msg>fonts: convert blob URLs to data URLs, to avoid local resource restrictions when loading from .zip bundles<commit_after>import log from '../../utils/log';
import Utils from '../../utils/utils';
import Texture from '../../gl/texture';

import FontFaceObserver from 'fontfaceobserver';

export default class CanvasText {

    constructor () {
        this.canvas = document.createElement('canvas');
        this.canvas.style.backgroundColor = 'transparent'; // render text on transparent background
        this.context = this.canvas.getContext('2d');
    }

    resize (width, height) {
        this.canvas.width = width;
        this.canvas.height = height;
        this.context.clearRect(0, 0, width, height);
    }

    // Set font style params for canvas drawing
    setFont ({ font_css, fill, stroke, stroke_width, px_size }) {
        this.px_size = px_size;
        this.text_buffer = 8; // pixel padding around text
        let ctx = this.context;

        ctx.font = font_css;
        if (stroke) {
            ctx.strokeStyle = stroke;
            ctx.lineWidth = stroke_width;
        }
        else {
            ctx.strokeStyle = null;
            ctx.lineWidth = 0;
        }
        ctx.fillStyle = fill;
        ctx.miterLimit = 2;
    }

    textSizes (texts) {
        return CanvasText.fonts_loaded.then(() => {
            for (let style in texts) {
                let text_infos = texts[style];
                let first = true;

                for (let text in text_infos) {
                    // Use cached size, or compute via canvas
                    if (!CanvasText.text_cache[style] || !CanvasText.text_cache[style][text]) {
                        let text_settings = text_infos[text].text_settings;
                        if (first) {
                            this.setFont(text_settings);
                            first = false;
                        }

                        CanvasText.text_cache[style] = CanvasText.text_cache[style] || {};
                        CanvasText.text_cache[style][text] =
                            this.textSize(text, text_settings.transform, text_settings.text_wrap);
                        CanvasText.cache_stats.misses++;
                    }
                    else {
                        CanvasText.cache_stats.hits++;
                    }

                    // Only send text sizes back to worker (keep computed text line info
                    // on main thread, for future rendering)
                    text_infos[text].size = CanvasText.text_cache[style][text].size;
                }
            }

            return texts;
        });
    }

    // Computes width and height of text based on current font style
    // Includes word wrapping, returns size info for whole text block and individual lines
    textSize (text, transform, text_wrap) {
        let str = this.applyTextTransform(text, transform);
        let ctx = this.context;
        let buffer = this.text_buffer * Utils.device_pixel_ratio;
        let leading = 2 * Utils.device_pixel_ratio; // make configurable and/or use Canvas TextMetrics when available
        let line_height = this.px_size + leading; // px_size already in device pixels

        // Word wrapping
        // Line breaks can be caused by:
        //  - implicit line break when a maximum character threshold is exceeded per line (text_wrap)
        //  - explicit line break in the label text (\n)
        let words;
        if (typeof text_wrap === 'number') {
            words = str.split(' '); // split words on spaces
        }
        else {
            words = [str]; // no max line word wrapping (but new lines will still be in effect)
        }
        let new_line_template = { width: 0, chars: 0, text: '' };
        let line = Object.assign({}, new_line_template); // current line
        let lines = []; // completed lines
        let max_width = 0; // max width to fit all lines

        // add current line buffer to completed lines, optionally start new line
        function addLine (new_line) {
            line.text = line.text.trim();
            if (line.text.length > 0) {
                line.width = ctx.measureText(line.text).width;
                max_width = Math.max(max_width, Math.ceil(line.width));
                lines.push(line);
            }
            if (new_line) {
                line = Object.assign({}, new_line_template);
            }
        }

        // First iterate on space-break groups (will be one if max line length off), then iterate on line-break groups
        for (let w=0; w < words.length; w++) {
            let breaks = words[w].split('\n'); // split on line breaks

            for (let n=0; n < breaks.length; n++) {
                let word = breaks[n];

                // if adding current word would overflow, add a new line instead
                if (line.chars + word.length > text_wrap && line.chars > 0) {
                    addLine(true);
                }

                // add current word (plus space)
                line.chars += word.length + 1;
                line.text += word + ' ';

                // if line breaks present, add new line (unless on last line)
                if (breaks.length > 1 && n < breaks.length - 1) {
                    addLine(true);
                }
            }
        }
        addLine(false);

        // Final dimensions of text
        let height = lines.length * line_height;

        let collision_size = [
            max_width / Utils.device_pixel_ratio,
            height / Utils.device_pixel_ratio
        ];

        let texture_size = [
            max_width + buffer * 2,
            height + buffer * 2
        ];

        let logical_size = texture_size.map(v => v / Utils.device_pixel_ratio);

        // Returns lines (w/per-line info for drawing) and text's overall bounding box + canvas size
        return {
            lines,
            size: { collision_size, texture_size, logical_size, line_height }
        };
    }

    // Draw one or more lines of text at specified location, adjusting for buffer and baseline
    drawText (lines, [x, y], size, { stroke, transform, align }) {
        align = align || 'center';

        for (let line_num=0; line_num < lines.length; line_num++) {
            let line = lines[line_num];
            let str = this.applyTextTransform(line.text, transform);
            let buffer = this.text_buffer * Utils.device_pixel_ratio;
            let texture_size = size.texture_size;
            let line_height = size.line_height;

            // Text alignment
            let tx;
            if (align === 'left') {
                tx = x + buffer;
            }
            else if (align === 'center') {
                tx = x + texture_size[0]/2 - line.width/2;
            }
            else if (align === 'right') {
                tx = x + texture_size[0] - line.width - buffer;
            }

            // In the absence of better Canvas TextMetrics (not supported by browsers yet),
            // 0.75 buffer produces a better approximate vertical centering of text
            let ty = y + buffer * 0.75 + (line_num + 1) * line_height;

            if (stroke) {
                this.context.strokeText(str, tx, ty);
            }
            this.context.fillText(str, tx, ty);
        }
    }

    rasterize (texts, texture_size) {
        for (let style in texts) {
            let text_infos = texts[style];
            let first = true;

            for (let text in text_infos) {
                let info = text_infos[text];
                let text_settings = info.text_settings;
                let lines = CanvasText.text_cache[style][text].lines; // get previously computed lines of text

                if (first) {
                    this.setFont(text_settings);
                    first = false;
                }

                this.drawText(lines, info.position, info.size, {
                    stroke: text_settings.stroke,
                    transform: text_settings.transform,
                    align: text_settings.align
                });

                info.texcoords = Texture.getTexcoordsForSprite(
                    info.position,
                    info.size.texture_size,
                    texture_size
                );
            }
        }
    }

    // Place text labels within an atlas of the given max size
    setTextureTextPositions (texts, max_texture_size) {
        // Find widest label
        let widest = 0;
        for (let style in texts) {
            let text_infos = texts[style];
            for (let text in text_infos) {
                let size = text_infos[text].size.texture_size;
                if (size[0] > widest) {
                    widest = size[0];
                }
            }
        }

        // Layout labels, stacked in columns
        let cx = 0, cy = 0; // current x/y position in atlas
        let height = 0;     // overall atlas height
        for (let style in texts) {
            let text_infos = texts[style];
            for (let text in text_infos) {
                let text_info = text_infos[text];
                let size = text_info.size.texture_size;
                if (cy + size[1] < max_texture_size) {
                    text_info.position = [cx, cy]; // add label to current column
                    cy += size[1];
                    if (cy > height) {
                        height = cy;
                    }
                }
                else { // start new column if taller than texture
                    cx += widest;
                    cy = 0;
                    text_info.position = [cx, cy];
                }
            }
        }

        return [cx + widest, height]; // overall atlas size
    }

    // Called before rasterization
    applyTextTransform (text, transform) {
        if (transform === 'capitalize') {
            return text.replace(/\w\S*/g, function (txt) {
                return txt.charAt(0).toUpperCase() + txt.substr(1);
            });
        }
        else if (transform === 'uppercase') {
            return text.toUpperCase();
        }
        else if (transform === 'lowercase') {
            return text.toLowerCase();
        }
        return text;
    }

    // Convert font CSS-style size ('12px', '14pt', '1.5em', etc.) to pixel size (adjusted for device pixel ratio)
    // Defaults units to pixels if not specified
    static fontPixelSize (size) {
        if (size == null) {
            return;
        }
        size = (typeof size === 'string') ? size : String(size); // need a string for regex

        let [, px_size, units] = size.match(CanvasText.font_size_re) || [];
        units = units || 'px';

        if (units === "em") {
            px_size *= 16;
        } else if (units === "pt") {
            px_size /= 0.75;
        } else if (units === "%") {
            px_size /= 6.25;
        }

        px_size = parseFloat(px_size);
        px_size *= Utils.device_pixel_ratio;
        return px_size;
    }

    // Load set of custom font faces
    // `fonts` is an object where the key is a font family name, and the value is one or more font face
    // definitions. The value can be either a single object, or an array of such objects.
    // If the special string value 'external' is used, it indicates the the font will be loaded via external CSS.
    static loadFonts (fonts) {
        let queue = [];
        for (let family in fonts) {
            if (Array.isArray(fonts[family])) {
                fonts[family].forEach(face => queue.push(this.loadFontFace(family, face)));
            }
            else {
                queue.push(this.loadFontFace(family, fonts[family]));
            }
        }

        CanvasText.fonts_loaded = Promise.all(queue.filter(x => x));
        return CanvasText.fonts_loaded;
    }

    // Load a single font face
    // `face` contains the font face definition, with optional parameters for `weight`, `style`, and `url`.
    // If the `url` is defined, the font is injected into the document as a CSS font-face.
    // If the object's value is the special string 'external', or if no `url` is defined, then the font face
    // is assumed is assumed to been loaded via external CSS. In either case, the function returns a promise
    // that resolves when the font face has loaded, or times out.
    static loadFontFace (family, face) {
        if (face == null || (typeof face !== 'object' && face !== 'external')) {
            return;
        }

        let options = { family };
        let inject = Promise.resolve();

        if (typeof face === 'object') {
            Object.assign(options, face);

            // If URL is defined, inject font into document
            if (typeof face.url === 'string') {
                inject = this.injectFontFace(options);
            }
        }

        // Wait for font to load
        let observer = new FontFaceObserver(family, options);
        return inject.then(() => observer.load()).then(
            () => {
                // Promise resolves, font is available
                log('debug', `Font face '${family}' is available`, options);
            },
            () => {
                // Promise rejects, font is not available
                log('debug', `Font face '${family}' is NOT available`, options);
            }
        );
    }

    // Loads a font face via CSS injection
    // TODO: consider support for multiple format URLs per face, unicode ranges
    static injectFontFace ({ family, url, weight, style }) {
        // Convert blob URLs to data URLs, to avoid local resource restrictions when loading from .zip bundles
        // ('Not allowed to load local resource: blob:...' console errors in Chrome)
        let preprocess = Promise.resolve(url);
        if (url.slice(0, 5) === 'blob:') {
            preprocess = Utils.io(url, 60000, 'arraybuffer').then(data => {
                let bytes = new Uint8Array(data);
                let str = '';

                for (let i = 0; i < bytes.length; i++) {
                    str += String.fromCharCode(bytes[i]);
                }
                return 'data:font/opentype;base64,' + btoa(str); // TODO: Set the correct font format.
            });
        }

        return preprocess.then(url => {
            let css = `
                @font-face {
                    font-family: '${family}';
                    font-weight: ${weight || 'normal'};
                    font-style: ${style || 'normal'};
                    src: url(${encodeURI(url)});
                }
            `;

            let style_el = document.createElement('style');
            style_el.appendChild(document.createTextNode(""));
            document.head.appendChild(style_el);
            style_el.sheet.insertRule(css, 0);
            log('trace', 'Injecting CSS font face:', css);
        });
    }

}

// Extract font size and units
CanvasText.font_size_re = /((?:[0-9]*\.)?[0-9]+)\s*(px|pt|em|%)?/;

// Cache sizes of rendered text
CanvasText.text_cache = {}; // by text style, then text string
CanvasText.cache_stats = { hits: 0, misses: 0 };

// Font detection
CanvasText.fonts_loaded = Promise.resolve(); // resolves when all requested fonts have been detected
<|endoftext|>


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

export default {
  name: 'offering',
  title: 'Offerings',
  icon: 'shopping',
  permission: ['listServiceOfferings'],
  children: [
    {
      name: 'computeoffering',
      title: 'Compute Offerings',
      icon: 'cloud',
      permission: ['listServiceOfferings'],
      params: { isrecursive: 'true' },
      columns: ['name', 'displaytext', 'cpunumber', 'cpuspeed', 'memory', 'tags', 'domain', 'zone', 'order'],
      details: ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'tags', 'domain', 'zone', 'created'],
      related: [{
        name: 'vm',
        title: 'Instances',
        param: 'serviceofferingid'
      }],
      actions: [{
        api: 'createServiceOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddComputeOffering.vue')
      }, {
        api: 'updateServiceOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext']
      }, {
        api: 'updateServiceOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteServiceOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    },
    {
      name: 'systemoffering',
      title: 'System Offerings',
      icon: 'setting',
      permission: ['listServiceOfferings', 'listInfrastructure'],
      params: { issystem: 'true', isrecursive: 'true' },
      columns: ['name', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'tags', 'order'],
      details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'tags', 'domain', 'zone', 'created'],
      actions: [{
        api: 'createServiceOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        params: { issystem: 'true' },
        popup: true,
        component: () => import('@/views/offering/AddComputeOffering.vue')
      }, {
        api: 'updateServiceOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        params: { issystem: 'true' },
        args: ['name', 'displaytext']
      }, {
        api: 'deleteServiceOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true,
        params: { issystem: 'true' }
      }]
    },
    {
      name: 'diskoffering',
      title: 'Disk Offerings',
      icon: 'hdd',
      permission: ['listDiskOfferings'],
      params: { isrecursive: 'true' },
      columns: ['name', 'displaytext', 'disksize', 'tags', 'domain', 'zone', 'order'],
      details: ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'tags', 'domain', 'zone', 'created'],
      related: [{
        name: 'volume',
        title: 'Volumes',
        param: 'diskofferingid'
      }],
      actions: [{
        api: 'createDiskOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddDiskOffering.vue')
      }, {
        api: 'updateDiskOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext']
      }, {
        api: 'updateDiskOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteDiskOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    },
    {
      name: 'networkoffering',
      title: 'Network Offerings',
      icon: 'wifi',
      permission: ['listNetworkOfferings'],
      params: { isrecursive: 'true' },
      columns: ['name', 'state', 'guestiptype', 'traffictype', 'networkrate', 'tags', 'domain', 'zone'],
      details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'service', 'tags', 'domain', 'zone'],
      actions: [{
        api: 'createNetworkOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddNetworkOffering.vue')
      }, {
        api: 'updateNetworkOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext', 'availability']
      }, {
        api: 'updateNetworkOffering',
        icon: 'play-circle',
        label: 'Enable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Disabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Enabled' }
          }
        }
      }, {
        api: 'updateNetworkOffering',
        icon: 'pause-circle',
        label: 'Disable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Enabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Disabled' }
          }
        }
      }, {
        api: 'updateNetworkOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteNetworkOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    },
    {
      name: 'vpcoffering',
      title: 'VPC Offerings',
      icon: 'deployment-unit',
      permission: ['listVPCOfferings'],
      params: { isrecursive: 'true' },
      resourceType: 'VpcOffering',
      columns: ['name', 'state', 'displaytext', 'domain', 'zone'],
      details: ['name', 'id', 'displaytext', 'distributedvpcrouter', 'service', 'tags', 'domain', 'zone', 'created'],
      related: [{
        name: 'vpc',
        title: 'VPCs',
        param: 'vpcofferingid'
      }],
      actions: [{
        api: 'createVPCOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddVpcOffering.vue')
      }, {
        api: 'updateVPCOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext']
      }, {
        api: 'updateVPCOffering',
        icon: 'play-circle',
        label: 'Enable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Disabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Enabled' }
          }
        }
      }, {
        api: 'updateVPCOffering',
        icon: 'pause-circle',
        label: 'Disable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Enabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Disabled' }
          }
        }
      }, {
        api: 'updateVPCOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteVPCOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    }
  ]
}
<commit_msg>offering: allow sort order for network offerings<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

export default {
  name: 'offering',
  title: 'Offerings',
  icon: 'shopping',
  permission: ['listServiceOfferings'],
  children: [
    {
      name: 'computeoffering',
      title: 'Compute Offerings',
      icon: 'cloud',
      permission: ['listServiceOfferings'],
      params: { isrecursive: 'true' },
      columns: ['name', 'displaytext', 'cpunumber', 'cpuspeed', 'memory', 'tags', 'domain', 'zone', 'order'],
      details: ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'tags', 'domain', 'zone', 'created'],
      related: [{
        name: 'vm',
        title: 'Instances',
        param: 'serviceofferingid'
      }],
      actions: [{
        api: 'createServiceOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddComputeOffering.vue')
      }, {
        api: 'updateServiceOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext']
      }, {
        api: 'updateServiceOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteServiceOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    },
    {
      name: 'systemoffering',
      title: 'System Offerings',
      icon: 'setting',
      permission: ['listServiceOfferings', 'listInfrastructure'],
      params: { issystem: 'true', isrecursive: 'true' },
      columns: ['name', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'tags', 'order'],
      details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'tags', 'domain', 'zone', 'created'],
      actions: [{
        api: 'createServiceOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        params: { issystem: 'true' },
        popup: true,
        component: () => import('@/views/offering/AddComputeOffering.vue')
      }, {
        api: 'updateServiceOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        params: { issystem: 'true' },
        args: ['name', 'displaytext']
      }, {
        api: 'deleteServiceOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true,
        params: { issystem: 'true' }
      }]
    },
    {
      name: 'diskoffering',
      title: 'Disk Offerings',
      icon: 'hdd',
      permission: ['listDiskOfferings'],
      params: { isrecursive: 'true' },
      columns: ['name', 'displaytext', 'disksize', 'tags', 'domain', 'zone', 'order'],
      details: ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'tags', 'domain', 'zone', 'created'],
      related: [{
        name: 'volume',
        title: 'Volumes',
        param: 'diskofferingid'
      }],
      actions: [{
        api: 'createDiskOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddDiskOffering.vue')
      }, {
        api: 'updateDiskOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext']
      }, {
        api: 'updateDiskOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteDiskOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    },
    {
      name: 'networkoffering',
      title: 'Network Offerings',
      icon: 'wifi',
      permission: ['listNetworkOfferings'],
      params: { isrecursive: 'true' },
      columns: ['name', 'state', 'guestiptype', 'traffictype', 'networkrate', 'tags', 'domain', 'zone', 'order'],
      details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'service', 'tags', 'domain', 'zone'],
      actions: [{
        api: 'createNetworkOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddNetworkOffering.vue')
      }, {
        api: 'updateNetworkOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext', 'availability']
      }, {
        api: 'updateNetworkOffering',
        icon: 'play-circle',
        label: 'Enable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Disabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Enabled' }
          }
        }
      }, {
        api: 'updateNetworkOffering',
        icon: 'pause-circle',
        label: 'Disable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Enabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Disabled' }
          }
        }
      }, {
        api: 'updateNetworkOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteNetworkOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    },
    {
      name: 'vpcoffering',
      title: 'VPC Offerings',
      icon: 'deployment-unit',
      permission: ['listVPCOfferings'],
      params: { isrecursive: 'true' },
      resourceType: 'VpcOffering',
      columns: ['name', 'state', 'displaytext', 'domain', 'zone'],
      details: ['name', 'id', 'displaytext', 'distributedvpcrouter', 'service', 'tags', 'domain', 'zone', 'created'],
      related: [{
        name: 'vpc',
        title: 'VPCs',
        param: 'vpcofferingid'
      }],
      actions: [{
        api: 'createVPCOffering',
        icon: 'plus',
        label: 'Add Offering',
        listView: true,
        popup: true,
        component: () => import('@/views/offering/AddVpcOffering.vue')
      }, {
        api: 'updateVPCOffering',
        icon: 'edit',
        label: 'Edit Offering',
        dataView: true,
        args: ['name', 'displaytext']
      }, {
        api: 'updateVPCOffering',
        icon: 'play-circle',
        label: 'Enable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Disabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Enabled' }
          }
        }
      }, {
        api: 'updateVPCOffering',
        icon: 'pause-circle',
        label: 'Disable Offering',
        dataView: true,
        show: (record) => { return record.state === 'Enabled' },
        args: ['state'],
        mapping: {
          state: {
            value: (record) => { return 'Disabled' }
          }
        }
      }, {
        api: 'updateVPCOffering',
        icon: 'lock',
        label: 'Update Offering Access',
        dataView: true,
        popup: true,
        component: () => import('@/views/offering/UpdateOfferingAccess.vue')
      }, {
        api: 'deleteVPCOffering',
        icon: 'delete',
        label: 'Delete Offering',
        dataView: true
      }]
    }
  ]
}
<|endoftext|>


<commit_before>exports = module.exports = function(container, store, logger) {
  var oauth2orize = require('oauth2orize');

  var server = oauth2orize.createServer({
    store: store
  });

  return Promise.resolve(server)
    .then(function(server) {
      // Register request parameter extensions with the OAuth 2.0 server.
      var paramDecls = container.components('http://i.authnomicon.org/oauth2/http/request/Parameters');

      return Promise.all(paramDecls.map(function(spec) { return container.create(spec.id); } ))
        .then(function(plugins) {
          plugins.forEach(function(plugin, i) {
            server.grant(plugin);
            logger.info('Loaded OAuth 2.0 request parameters: ' + paramDecls[i].id);
          });
        })
        .then(function() {
          return server;
        });
    })
    .then(function(server) {
      // Register response types with the OAuth 2.0 server.
      var typeComps = container.components('http://i.authnomicon.org/oauth2/http/Response')

      return Promise.all(typeComps.map(function(spec) { return container.create(spec.id); } ))
        .then(function(plugins) {
          plugins.forEach(function(plugin, i) {
            server.grant(typeComps[i].a['@type'] || plugin.name, plugin);
            logger.info('Loaded OAuth 2.0 response type: ' + (typeComps[i].a['@type'] || plugin.name));
          });
        })
        .then(function() {
          return server;
        });
    })
    .then(function(server) {
      // Register grant types with the OAuth 2.0 server.
      var typeComps = container.components('http://i.authnomicon.org/oauth2/http/Exchange')

      return Promise.all(typeComps.map(function(spec) { return container.create(spec.id); } ))
        .then(function(plugins) {
          plugins.forEach(function(plugin, i) {
            server.exchange(typeComps[i].a['@type'] || plugin.name, plugin);
            logger.info('Loaded OAuth 2.0 grant type: ' + (typeComps[i].a['@type'] || plugin.name));
          });
        })
        .then(function() {
          return server;
        });
    })
    .then(function(server) {
      return server;
    });
}

exports['@implements'] = 'http://schemas.authnomicon.org/js/oauth2/Server';
exports['@singleton'] = true;
exports['@require'] = [
  '!container',
  './transactionstore',
  'http://i.bixbyjs.org/Logger'
];
<commit_msg>Clean up exchange loading.<commit_after>exports = module.exports = function(container, store, logger) {
  var oauth2orize = require('oauth2orize');

  var server = oauth2orize.createServer({
    store: store
  });

  return Promise.resolve(server)
    .then(function(server) {
      // Register request parameter extensions with the OAuth 2.0 server.
      var paramDecls = container.components('http://i.authnomicon.org/oauth2/http/request/Parameters');

      return Promise.all(paramDecls.map(function(spec) { return container.create(spec.id); } ))
        .then(function(plugins) {
          plugins.forEach(function(plugin, i) {
            server.grant(plugin);
            logger.info('Loaded OAuth 2.0 request parameters: ' + paramDecls[i].id);
          });
        })
        .then(function() {
          return server;
        });
    })
    .then(function(server) {
      // Register response types with the OAuth 2.0 server.
      var typeComps = container.components('http://i.authnomicon.org/oauth2/http/Response')

      return Promise.all(typeComps.map(function(spec) { return container.create(spec.id); } ))
        .then(function(plugins) {
          plugins.forEach(function(plugin, i) {
            server.grant(typeComps[i].a['@type'] || plugin.name, plugin);
            logger.info('Loaded OAuth 2.0 response type: ' + (typeComps[i].a['@type'] || plugin.name));
          });
        })
        .then(function() {
          return server;
        });
    })
    .then(function(server) {
      // Register grant types with the OAuth 2.0 server.

      return new Promise(function(resolve, reject) {
        var components = container.components('http://i.authnomicon.org/oauth2/http/Exchange');

        (function iter(i) {
          var component = components[i]
            , type;
          if (!component) {
            return resolve(server);
          }

          type = component.a['@type'];

          component.create()
            .then(function(exchange) {
              logger.info('Loaded OAuth 2.0 grant exchange: ' + (type || exchange.name));
              server.exchange(type || exchange.name, exchange);
              iter(i + 1);
            }, function(err) {
              // TODO: Print the package name in the error, so it can be found
              // TODO: Make the error have the stack of dependencies.
              if (err.code == 'IMPLEMENTATION_NOT_FOUND') {
                // TODO: Mount debugging middleware here

                logger.notice(err.message + ' while loading component ' + component.id);
                return iter(i + 1);
              }

              reject(err);
            })
        })(0);
      });
    })
    .then(function(server) {
      return server;
    });
}

exports['@implements'] = 'http://schemas.authnomicon.org/js/oauth2/Server';
exports['@singleton'] = true;
exports['@require'] = [
  '!container',
  './transactionstore',
  'http://i.bixbyjs.org/Logger'
];
<|endoftext|>


<commit_before>if (!KAPhy) {
  var KAPhy = {
    current: "0.0.5",
    install: function() {
      if (!KAPhy.installed) {
        KAPhy.installed = true;
        KAPhy.version = KAPhy.current;
      } else if (KAPhy.version !== KAPhy.current) {
        console.log("KAPhy is updating.");
      }
    },
    finishUpdate: function() {
      if (KAPhy.version !== KAPhy.current) {
        KAPhy.version = KAPhy.current;
        console.log("KAPhy update finished!");
      }
      console.log("KAPhy loaded!");
    }
  };
}
KAPhy.install();
<commit_msg>Update kaphy.js<commit_after>if (!KAPhy) {
  var KAPhy = {
    current: "0.0.5",
    install: function() {
      if (KAPhy.version !== KAPhy.current) {
        console.log("KAPhy is updating or installing.");
      }
    },
    finishUpdate: function() {
      if (KAPhy.version !== KAPhy.current) {
        KAPhy.version = KAPhy.current;
        console.log("KAPhy update finished!");
      }
      console.log("KAPhy loaded!");
    }
  };
}
KAPhy.install();
<|endoftext|>


<commit_before>// Higher order component that force updates component on themer change
// and passes down theme through props
import React, { Component } from 'react'
import themer from './index'
import { themePropTypes } from './utils'

function withTheme(InnerComponent) {
  class Wrapped extends Component {
    static displayName = `withTheme(${InnerComponent.name || InnerComponent.displayName || 'Component'})`

    static propTypes = {
      snacksTheme: themePropTypes
    }

    componentDidMount() {
      this.unsubscribe = themer.subscribe(this.onThemeChange)
    }

    componentWillUnmount() {
      this.unsubscribe()
    }

    onThemeChange = () => {
      this.forceUpdate()
    }

    render() {
      return (
        <InnerComponent
          {...this.props}
          snacksTheme={themer.themeConfig}
        />
      )
    }
  }

  return Wrapped
}

export default withTheme
<commit_msg>Fix issue withTheme and refs (#142)<commit_after>// Higher order component that force updates component on themer change
// and passes down theme through props
import React, { Component } from 'react'
import themer from './index'
import { themePropTypes } from './utils'

function withTheme(InnerComponent) {
  class Wrapped extends Component {
    static displayName = `withTheme(${InnerComponent.name || InnerComponent.displayName || 'Component'})`

    static propTypes = {
      snacksTheme: themePropTypes
    }

    componentDidMount() {
      this.unsubscribe = themer.subscribe(this.onThemeChange)
    }

    componentWillUnmount() {
      this.unsubscribe()
    }

    onThemeChange = () => {
      this.forceUpdate()
    }

    render() {
      return (
        <InnerComponent
          ref={node => this.wrapped = node}
          {...this.props}
          snacksTheme={themer.themeConfig}
        />
      )
    }
  }

  return Wrapped
}

export default withTheme
<|endoftext|>


<commit_before>var m = require('mithril');

var scale = 8;
var now;
var startTime;
var stopTime;

function leftPos(time) {
  return scale * (time - startTime) / 1000 / 60;
}

function scrollToNow(el) {
  el.scrollLeft = leftPos(now - el.clientWidth / 2 / scale * 60 * 1000);
}

function speedGrouper(t) {
  if (t.schedule && t.schedule.speed === 'superblitz') {
    return t.perf.position - 0.5;
  } else {
    return t.perf.position;
  }
}

function group(arr, grouper) {
  var groups = {};
  arr.forEach(function(e) {
    var g = grouper(e);
    if (!groups[g]) groups[g] = [];
    groups[g].push(e);
  });
  return Object.keys(groups).sort().map(function(k) {
    return groups[k];
  });
}

function fitLane(lane, tour2) {
  return !lane.some(function(tour1) {
    return !(tour1.finishesAt <= tour2.startsAt || tour2.finishesAt <= tour1.startsAt);
  });
}

// splits lanes that have collisions, but keeps
// groups separate by not compacting existing lanes
function splitOverlaping(lanes) {
  var ret = [];
  lanes.forEach(function(lane) {
    var newLanes = [
      []
    ];
    lane.forEach(function(tour) {
      var collision = true;
      for (var i = 0; i < newLanes.length; i++) {
        if (fitLane(newLanes[i], tour)) {
          newLanes[i].push(tour);
          collision = false;
          break;
        }
      }
      if (collision) newLanes.push([tour]);
    });
    ret = ret.concat(newLanes);
  });
  return ret;
}

function renderTournament(ctrl, tour) {
  var width = tour.minutes * scale;
  var left = leftPos(tour.startsAt);
  // moves content into viewport, for long tourneys and marathons
  var paddingLeft = tour.minutes < 90 ? 0 : Math.max(0,
    Math.min(width - 250, // max padding, reserved text space
      leftPos(now) - left - 380)); // distance from Now
  // cut right overflow to fit viewport and not widen it, for marathons
  width = Math.min(width, leftPos(stopTime) - left);

  return m('a.tournament', {
    href: '/tournament/' + tour.id,
    style: {
      width: width + 'px',
      left: left + 'px',
      paddingLeft: paddingLeft + 'px'
    },
    class: (tour.schedule ? tour.schedule.freq : '') +
      (tour.createdBy === 'lichess' ? ' system' : ' user-created') +
      (tour.rated ? ' rated' : ' casual') +
      (tour.minutes <= 30 ? ' short' : '') +
      (tour.position ? ' thematic' : '') +
      (tour.status === 30 ? ' finished' : ' joinable')
  }, [
    m('span.icon', tour.perf ? {
      'data-icon': tour.perf.icon,
      title: tour.perf.name
    } : null),
    m('span.name', tour.fullName),
    m('span.clock', tour.clock.limit / 60 + "+" + tour.clock.increment),
    m('span', tour.rated ? ctrl.trans('rated') : ctrl.trans('casual')),
    tour.position ? m('span', 'Thematic') : null,
    tour.nbPlayers ? m('span.nb-players.text', {
      'data-icon': 'r'
    }, tour.nbPlayers) : null,
  ]);
}

function renderTimeline() {
  var minutesBetween = 10;
  var time = new Date(startTime);
  time.setSeconds(0);
  time.setMinutes(Math.floor(time.getMinutes() / minutesBetween) * minutesBetween);

  var timeHeaders = [];
  while (time.getTime() < (stopTime - minutesBetween * 60 * 1000)) {
    timeHeaders.push(m('div', {
      class: 'timeheader' + (time.getMinutes() === 0 ? ' hour' : ''),
      style: {
        left: leftPos(time.getTime()) + 'px'
      }
    }, timeString(time)));
    time.setMinutes(time.getMinutes() + minutesBetween);
  }

  return m('div.timeline',
    timeHeaders,
    m('div.timeheader.now', {
      style: {
        left: leftPos(now) + 'px'
      }
    })
  );
}

// converts Date to "%H:%M" with leading zeros
function timeString(time) {
  return ('0' + time.getHours()).slice(-2) + ":" + ('0' + time.getMinutes()).slice(-2);
}

function not(f) {
  return function(x) {
    return !f(x);
  };
}

function isSystemTournament(t) {
  return t.createdBy === 'lichess';
}

module.exports = function(ctrl) {
  now = (new Date()).getTime();
  startTime = now - 3 * 60 * 60 * 1000;
  stopTime = startTime + 8 * 60 * 60 * 1000;

  if (!ctrl.data.systemTours) {
    var tours = ctrl.data.finished.concat(ctrl.data.started).concat(ctrl.data.created);
    ctrl.data.systemTours = tours.filter(isSystemTournament);
    ctrl.data.userTours = tours.filter(not(isSystemTournament));
  }

  // group system tournaments into dedicated lanes for PerfType
  var tourLanes = splitOverlaping(
    group(ctrl.data.systemTours, speedGrouper).concat([ctrl.data.userTours]));

  return m('div.schedule.dragscroll', {
    config: function(el, isUpdate) {
      if (!isUpdate) scrollToNow(el);
    }
  }, [
    renderTimeline(),
    tourLanes.filter(function(lane) {
      return lane.length > 0;
    }).map(function(lane) {
      return m('div.tournamentline',
        lane.map(function(tour) {
          return renderTournament(ctrl, tour);
        }));
    })
  ]);
};
<commit_msg>extend tourney schedule view<commit_after>var m = require('mithril');

var scale = 8;
var now;
var startTime;
var stopTime;

function leftPos(time) {
  return scale * (time - startTime) / 1000 / 60;
}

function scrollToNow(el) {
  el.scrollLeft = leftPos(now - el.clientWidth / 2 / scale * 60 * 1000);
}

function speedGrouper(t) {
  if (t.schedule && t.schedule.speed === 'superblitz') {
    return t.perf.position - 0.5;
  } else {
    return t.perf.position;
  }
}

function group(arr, grouper) {
  var groups = {};
  arr.forEach(function(e) {
    var g = grouper(e);
    if (!groups[g]) groups[g] = [];
    groups[g].push(e);
  });
  return Object.keys(groups).sort().map(function(k) {
    return groups[k];
  });
}

function fitLane(lane, tour2) {
  return !lane.some(function(tour1) {
    return !(tour1.finishesAt <= tour2.startsAt || tour2.finishesAt <= tour1.startsAt);
  });
}

// splits lanes that have collisions, but keeps
// groups separate by not compacting existing lanes
function splitOverlaping(lanes) {
  var ret = [];
  lanes.forEach(function(lane) {
    var newLanes = [
      []
    ];
    lane.forEach(function(tour) {
      var collision = true;
      for (var i = 0; i < newLanes.length; i++) {
        if (fitLane(newLanes[i], tour)) {
          newLanes[i].push(tour);
          collision = false;
          break;
        }
      }
      if (collision) newLanes.push([tour]);
    });
    ret = ret.concat(newLanes);
  });
  return ret;
}

function renderTournament(ctrl, tour) {
  var width = tour.minutes * scale;
  var left = leftPos(tour.startsAt);
  // moves content into viewport, for long tourneys and marathons
  var paddingLeft = tour.minutes < 90 ? 0 : Math.max(0,
    Math.min(width - 250, // max padding, reserved text space
      leftPos(now) - left - 380)); // distance from Now
  // cut right overflow to fit viewport and not widen it, for marathons
  width = Math.min(width, leftPos(stopTime) - left);

  return m('a.tournament', {
    href: '/tournament/' + tour.id,
    style: {
      width: width + 'px',
      left: left + 'px',
      paddingLeft: paddingLeft + 'px'
    },
    class: (tour.schedule ? tour.schedule.freq : '') +
      (tour.createdBy === 'lichess' ? ' system' : ' user-created') +
      (tour.rated ? ' rated' : ' casual') +
      (tour.minutes <= 30 ? ' short' : '') +
      (tour.position ? ' thematic' : '') +
      (tour.status === 30 ? ' finished' : ' joinable')
  }, [
    m('span.icon', tour.perf ? {
      'data-icon': tour.perf.icon,
      title: tour.perf.name
    } : null),
    m('span.name', tour.fullName),
    m('span.clock', tour.clock.limit / 60 + "+" + tour.clock.increment),
    m('span', tour.rated ? ctrl.trans('rated') : ctrl.trans('casual')),
    tour.position ? m('span', 'Thematic') : null,
    tour.nbPlayers ? m('span.nb-players.text', {
      'data-icon': 'r'
    }, tour.nbPlayers) : null,
  ]);
}

function renderTimeline() {
  var minutesBetween = 10;
  var time = new Date(startTime);
  time.setSeconds(0);
  time.setMinutes(Math.floor(time.getMinutes() / minutesBetween) * minutesBetween);

  var timeHeaders = [];
  while (time.getTime() < (stopTime - minutesBetween * 60 * 1000)) {
    timeHeaders.push(m('div', {
      class: 'timeheader' + (time.getMinutes() === 0 ? ' hour' : ''),
      style: {
        left: leftPos(time.getTime()) + 'px'
      }
    }, timeString(time)));
    time.setMinutes(time.getMinutes() + minutesBetween);
  }

  return m('div.timeline',
    timeHeaders,
    m('div.timeheader.now', {
      style: {
        left: leftPos(now) + 'px'
      }
    })
  );
}

// converts Date to "%H:%M" with leading zeros
function timeString(time) {
  return ('0' + time.getHours()).slice(-2) + ":" + ('0' + time.getMinutes()).slice(-2);
}

function not(f) {
  return function(x) {
    return !f(x);
  };
}

function isSystemTournament(t) {
  return t.createdBy === 'lichess';
}

module.exports = function(ctrl) {
  now = (new Date()).getTime();
  startTime = now - 3 * 60 * 60 * 1000;
  stopTime = startTime + 10 * 60 * 60 * 1000;

  if (!ctrl.data.systemTours) {
    var tours = ctrl.data.finished.concat(ctrl.data.started).concat(ctrl.data.created);
    ctrl.data.systemTours = tours.filter(isSystemTournament);
    ctrl.data.userTours = tours.filter(not(isSystemTournament));
  }

  // group system tournaments into dedicated lanes for PerfType
  var tourLanes = splitOverlaping(
    group(ctrl.data.systemTours, speedGrouper).concat([ctrl.data.userTours]));

  return m('div.schedule.dragscroll', {
    config: function(el, isUpdate) {
      if (!isUpdate) scrollToNow(el);
    }
  }, [
    renderTimeline(),
    tourLanes.filter(function(lane) {
      return lane.length > 0;
    }).map(function(lane) {
      return m('div.tournamentline',
        lane.map(function(tour) {
          return renderTournament(ctrl, tour);
        }));
    })
  ]);
};
<|endoftext|>


<commit_before>/**
 * AJ Framework main module. Contains functions to create hybrid applications using flux framework
 * @module aj
 */

"use strict";

const _ = require("../framework/underscore")
const Observable = require("../framework/events").Observable;
const Promise = require("../framework/promise");

var __runtime = null;
var __stores = {};
var __actions = {};

class AJRuntime {
    constructor() {

    }

    static instance() {
        if (!__runtime) {
            throw "Runtime not initialized"
        }
        return __runtime;
    }

    exec() {
        throw "Not implemented"
    }

    createBuffer(data) {
        throw "Not implemented"
    }

    loadBuffer(id) {
        throw "Not implemented"
    }

    destroyBuffer(id) {
        throw "Not implemented"
    }

    __trigger(store, state) {
        throw "Not implemented";
    }
}

if (platform.engine == "node") {
    (function() {
        var vm = require("vm");
        var fs = require("fs");

        class AJWebSocketServerRuntime extends AJRuntime {
            constructor() {
                super();

                this.semaphores = [];

                logger.i("New websocket server runtime created");
            }

            init(options) {
                this.socket =  options.socket;
                if (!this.socket) {
                    throw "Socket is required";
                }

                this.socket.on("run", (action, json, ack) => {
                    async(() => {
                        try {
                            var data = JSON.parse(json);
                            run(action, data);

                            ack();
                        } catch (e) {
                            if (e && e.stack) { logger.i(e.stack); }
                            logger.e(e);
                        }
                    });
                });

                this.socket.on("freeSemaphore", (id, data) => {
                    try {
                        this.freeSemaphore(id, data);
                    } catch(e) {
                        if (e && e.stack) { logger.i(e.stack); }
                        logger.e(e);
                    }
                });

                this.socket.on("error", (e) => {
                    if (e && e.stack) { logger.i(e.stack); }
                    logger.e(e);
                });
            }

            exec(plugin, fn, data) {
                logger.i("Executing plugin ", plugin + "." + fn);

                return new Promise((resolve, reject) => {
                    try {
                        this.socket.emit("exec", plugin, fn, data, (result) => {
                            resolve(result);
                        });
                    } catch (e) {
                        reject(e);
                    }
                })
            }

            __trigger(store, state) {
                logger.i("Triggering ", store);

                return new Promise((resolve, reject) => {
                    this.socket.emit("trigger", store, state, function() {
                        resolve();
                    });
                });

            }

            freeSemaphore(id, data) {
                var index = -1;
                var found = false;
                for (var i = 0; i < this.semaphores.length; i++) {
                    index++;
                    var semaphore = this.semaphores[i];

                    if (semaphore.id == id) {
                        found = true;
                        semaphore.free(data);
                        break;
                    }
                }

                if (found) {
                    this.semaphores.splice(index, 1);
                    logger.i("Semaphore destroyed:", semaphore.name);
                }
            }

            createBuffer(data) {
                return new Promise((resolve, reject) => {
                    this.socket.emit("createBuffer", data, (error, id) => {
                        if (!error) {
                            resolve(id)
                        } else {
                            reject()
                        }
                    })
                })
            };

            readBuffer(id) {
                return new Promise((resolve, reject) => {
                    this.socket.emit("readBuffer", id, (error, data) => {
                        if (!error) {
                            resolve(data)
                        } else {
                            reject()
                        }
                    })
                })
            };

            destroyBuffer(id) {
                return new Promise((resolve, reject) => {
                    this.socket.emit("readBuffer", id, (error) => {
                        if (!error) {
                            resolve()
                        } else {
                            reject()
                        }
                    })
                })
            };

        }

        AJRuntime.create = function() {
            return new AJWebSocketServerRuntime();
        }
    })();
} else {
    (function() {
        class AJNativeServerRuntime extends AJRuntime {
            constructor() {
                super();

                logger.i("New native server runtime created");
            }

            init(options) {

            }

            run(action, data) {
                run(action, data);
            }

            __trigger(store, state) {
                if (__trigger == undefined) {
                    throw "__trigger function not defined";
                }

                logger.i("Triggering ", store);

                return new Promise((resolve, reject) => {
                    async(() => {
                        try {
                            __trigger(store, state);
                            resolve();
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            }

            exec(plugin, fn, data) {
                if (__exec == undefined) {
                    throw "__exec function not defined";
                }

                logger.i("Executing plugin", plugin + "." + fn);

                return new Promise((resolve, reject) => {
                    async(() => {
                        try {
                            var result = __exec(plugin, fn, data);
                            logger.i("Plugin called with res:", result);

                            resolve(result);
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            }

            createBuffer(data) {
                return new Promise((resolve, reject) => {
                    __buffersManager.create(data, function(error, value) {
                        if (error) {
                            reject(value);
                        } else {
                            resolve(value);
                        }
                    });
                })
            };

            readBuffer(id) {
                return new Promise((resolve, reject) => {
                    __buffersManager.read(id, function(error, value) {
                        if (error) {
                            reject(value);
                        } else {
                            resolve(value);
                        }
                    });
                })
            };

            destroyBuffer(id) {
                return new Promise((resolve, reject) => {
                    __buffersManager.destroy(id, function(error, value) {
                        if (error) {
                            reject(value);
                        } else {
                            resolve(value);
                        }
                    });
                })
            };

        }

        AJRuntime.create = function() {
            return new AJNativeServerRuntime();
        }
    })();
}

class Store extends Observable {
    constructor(type, reducer) {
        super();

        this.type = type;
        this.reducer = reducer;
    }

    init(options) {}

    trigger(state) {
        if (state == undefined) {
            return __runtime.__trigger(this.type, this.state);
        } else {
            return __runtime.__trigger(this.type, state);
        }

    }

    dispatch(action) {
        if (_.isFunction(this.reducer)) {
            var newState = this.reducer(this.state, action);
            if (newState) {
                this.state = newState;

                this.trigger()
            }
        } else {
            logger.w("Cannot dispatch action:", this.type + "." + action);
        }
    }
}

class Semaphore {
    constructor(action) {
        this.complete = false;
        this.listeners = [];
        this.id = Semaphore.counter++;

        if (action) {
            this.runAction(action);
        }

    }

    runAction(action) {
        async(() => {
            action();
            this.free()
        })
    }

    then(action) {
        this.listeners.push(action);

        if (this.complete) {
            action();
        }

        return this;
    }

    free(data) {
        this.listeners.forEach((l) => { l(data); });
        this.complete = true;

        return this;
    }

}

Semaphore.counter = 1;

var createRuntime = function(options) {
    __runtime = AJRuntime.create();
    __runtime.init(options);

    return __runtime;
};

function createStore(type, reducer) {
    if (_.has(__stores, type)) {
        throw "Cannot create store " + type + ". Only one instance of store is allowed";
    }

    var store = new Store(type, reducer);
    __stores[type] = store;

    logger.i("Store created:", type);
}

function createAction(type, fn) {
    if (_.has(__actions, type)) {
        throw "Cannot create action " + type + ". Already created";
    }

    var act = __actions[type] = (data) => {
        fn(data);
    };

    logger.i("Action created:", type);

    return act;
}

function dispatch(action) {
    logger.i("Dispatching action", action);

    _.each(__stores, (store) => {
        try {
            store.dispatch(action);
        } catch (e) {
            if (e && e.stack) { logger.i(e.stack); }
            logger.e(e);
        }
    });
}

function run(action, data) {
    logger.i("Running action", action);

    if (_.has(__actions, action)) {
        __actions[action](data);
    } else {
        logger.w("Cannot find action: " + action);
    }
}

function exec(plugin, fn, data) {
    return __runtime.exec(plugin, fn, data);
}

/**
 * Creates a new instance of runtime. Usually used internally by devices runtimes
 * @returns singleton instance of runtime
 */
export const createRuntime = createRuntime;

/**
 * Creates a new singleton instance of store
 * @param {string} type - Name of store to create
 * @param {function} reducer - Store reducer
 */
export const createStore = createStore;

/**
 * Creates a new action for the application
 * @param {string} type - Type of action to create
 * @param {function} action - Action to execute
 * @returns {function} the newly created action
 */
export const createAction = createAction;

/**
 * Dispatch action to stores, usually called by actions
 * @param {object} data - Data to pass to stores
 */
export const dispatch = dispatch;

/**
 * Exec a plugin action
 * @param {string} plugin - The plugin
 * @param {action} action - The plugin action to call
 * @param {data} data - Data to pass to plugin
 */
export const exec = exec;

/**
 * Run specified action. This is not the common method to call actions, but it's necessary for managing actions from
 * devices. On JS side, call actions directly
 * @param {type} type - Type of action to call
 * @param {data} type - Data to pass to action
 */
export const run = run;
<commit_msg>Bug fixing<commit_after>/**
 * AJ Framework main module. Contains functions to create hybrid applications using flux framework
 * @module aj
 */

"use strict";

const _ = require("../framework/underscore")
const Observable = require("../framework/events").Observable;
const Promise = require("../framework/promise");

var __runtime = null;
var __stores = {};
var __actions = {};

class AJRuntime {
    constructor() {

    }

    static instance() {
        if (!__runtime) {
            throw "Runtime not initialized"
        }
        return __runtime;
    }

    exec() {
        throw "Not implemented"
    }

    createBuffer(data) {
        throw "Not implemented"
    }

    loadBuffer(id) {
        throw "Not implemented"
    }

    destroyBuffer(id) {
        throw "Not implemented"
    }

    __trigger(store, state) {
        throw "Not implemented";
    }
}

if (platform.engine == "node") {
    (function() {
        var vm = require("vm");
        var fs = require("fs");

        class AJWebSocketServerRuntime extends AJRuntime {
            constructor() {
                super();

                this.semaphores = [];

                logger.i("New websocket server runtime created");
            }

            init(options) {
                this.socket =  options.socket;
                if (!this.socket) {
                    throw "Socket is required";
                }

                this.socket.on("run", (action, json, ack) => {
                    async(() => {
                        try {
                            var data = JSON.parse(json);
                            run(action, data);

                            ack();
                        } catch (e) {
                            if (e && e.stack) { logger.i(e.stack); }
                            logger.e(e);
                        }
                    });
                });

                this.socket.on("freeSemaphore", (id, data) => {
                    try {
                        this.freeSemaphore(id, data);
                    } catch(e) {
                        if (e && e.stack) { logger.i(e.stack); }
                        logger.e(e);
                    }
                });

                this.socket.on("error", (e) => {
                    if (e && e.stack) { logger.i(e.stack); }
                    logger.e(e);
                });
            }

            exec(plugin, fn, data) {
                logger.i("Executing plugin ", plugin + "." + fn);

                return new Promise((resolve, reject) => {
                    try {
                        this.socket.emit("exec", plugin, fn, data, (result) => {
                            resolve(result);
                        });
                    } catch (e) {
                        reject(e);
                    }
                })
            }

            __trigger(store, state) {
                logger.i("Triggering ", store);

                return new Promise((resolve, reject) => {
                    this.socket.emit("trigger", store, state, function() {
                        resolve();
                    });
                });

            }

            freeSemaphore(id, data) {
                var index = -1;
                var found = false;
                for (var i = 0; i < this.semaphores.length; i++) {
                    index++;
                    var semaphore = this.semaphores[i];

                    if (semaphore.id == id) {
                        found = true;
                        semaphore.free(data);
                        break;
                    }
                }

                if (found) {
                    this.semaphores.splice(index, 1);
                    logger.i("Semaphore destroyed:", semaphore.name);
                }
            }

            createBuffer(data) {
                return new Promise((resolve, reject) => {
                    this.socket.emit("createBuffer", data, (error, id) => {
                        if (!error) {
                            resolve(id)
                        } else {
                            reject()
                        }
                    })
                })
            };

            readBuffer(id) {
                return new Promise((resolve, reject) => {
                    this.socket.emit("readBuffer", id, (error, data) => {
                        if (!error) {
                            resolve(data)
                        } else {
                            reject()
                        }
                    })
                })
            };

            destroyBuffer(id) {
                return new Promise((resolve, reject) => {
                    this.socket.emit("readBuffer", id, (error) => {
                        if (!error) {
                            resolve()
                        } else {
                            reject()
                        }
                    })
                })
            };

        }

        AJRuntime.create = function() {
            return new AJWebSocketServerRuntime();
        }
    })();
} else {
    (function() {
        class AJNativeServerRuntime extends AJRuntime {
            constructor() {
                super();

                logger.i("New native server runtime created");
            }

            init(options) {

            }

            run(action, data) {
                run(action, data);
            }

            __trigger(store, state) {
                if (__trigger == undefined) {
                    throw "__trigger function not defined";
                }

                logger.i("Triggering ", store);

                return new Promise((resolve, reject) => {
                    async(() => {
                        try {
                            __trigger(store, state);
                            resolve();
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            }

            exec(plugin, fn, data) {
                if (__exec == undefined) {
                    throw "__exec function not defined";
                }

                logger.i("Executing plugin", plugin + "." + fn);

                return new Promise((resolve, reject) => {
                    async(() => {
                        try {
                            var result = __exec(plugin, fn, data);
                            logger.i("Plugin called with res:", result);

                            resolve(result);
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            }

            createBuffer(data) {
                return new Promise((resolve, reject) => {
                    __buffersManager.create(data, function(error, value) {
                        if (error) {
                            reject(value);
                        } else {
                            resolve(value);
                        }
                    });
                })
            };

            readBuffer(id) {
                return new Promise((resolve, reject) => {
                    __buffersManager.read(id, function(error, value) {
                        if (error) {
                            reject(value);
                        } else {
                            resolve(value);
                        }
                    });
                })
            };

            destroyBuffer(id) {
                return new Promise((resolve, reject) => {
                    __buffersManager.destroy(id, function(error, value) {
                        if (error) {
                            reject(value);
                        } else {
                            resolve(value);
                        }
                    });
                })
            };

        }

        AJRuntime.create = function() {
            return new AJNativeServerRuntime();
        }
    })();
}

class Store extends Observable {
    constructor(type, reducer) {
        super();

        this.type = type;
        this.reducer = reducer;
    }

    init(options) {}

    trigger(state) {
        if (state == undefined) {
            return __runtime.__trigger(this.type, this.state);
        } else {
            return __runtime.__trigger(this.type, state);
        }

    }

    dispatch(action) {
        if (_.isFunction(this.reducer)) {
            var newState = this.reducer(this.state, action);
            if (newState) {
                this.state = newState;

                this.trigger()
            }
        } else {
            logger.w("Cannot dispatch action:", this.type + "." + action);
        }
    }
}

class Semaphore {
    constructor(action) {
        this.complete = false;
        this.listeners = [];
        this.id = Semaphore.counter++;

        if (action) {
            this.runAction(action);
        }

    }

    runAction(action) {
        async(() => {
            action();
            this.free()
        })
    }

    then(action) {
        this.listeners.push(action);

        if (this.complete) {
            action();
        }

        return this;
    }

    free(data) {
        this.listeners.forEach((l) => { l(data); });
        this.complete = true;

        return this;
    }

}

Semaphore.counter = 1;

function createRuntime(options) {
    __runtime = AJRuntime.create();
    __runtime.init(options);

    return __runtime;
};

function createStore(type, reducer) {
    if (_.has(__stores, type)) {
        throw "Cannot create store " + type + ". Only one instance of store is allowed";
    }

    var store = new Store(type, reducer);
    __stores[type] = store;

    logger.i("Store created:", type);
}

function createAction(type, fn) {
    if (_.has(__actions, type)) {
        throw "Cannot create action " + type + ". Already created";
    }

    var act = __actions[type] = (data) => {
        fn(data);
    };

    logger.i("Action created:", type);

    return act;
}

function dispatch(action) {
    logger.i("Dispatching action", action);

    _.each(__stores, (store) => {
        try {
            store.dispatch(action);
        } catch (e) {
            if (e && e.stack) { logger.i(e.stack); }
            logger.e(e);
        }
    });
}

function run(action, data) {
    logger.i("Running action", action);

    if (_.has(__actions, action)) {
        __actions[action](data);
    } else {
        logger.w("Cannot find action: " + action);
    }
}

function exec(plugin, fn, data) {
    return __runtime.exec(plugin, fn, data);
}

/**
 * Creates a new instance of runtime. Usually used internally by devices runtimes
 * @returns singleton instance of runtime
 */
export var createRuntime = createRuntime;

/**
 * Creates a new singleton instance of store
 * @param {string} type - Name of store to create
 * @param {function} reducer - Store reducer
 */
export var createStore = createStore;

/**
 * Creates a new action for the application
 * @param {string} type - Type of action to create
 * @param {function} action - Action to execute
 * @returns {function} the newly created action
 */
export var createAction = createAction;

/**
 * Dispatch action to stores, usually called by actions
 * @param {object} data - Data to pass to stores
 */
export var dispatch = dispatch;

/**
 * Exec a plugin action
 * @param {string} plugin - The plugin
 * @param {action} action - The plugin action to call
 * @param {data} data - Data to pass to plugin
 */
export var exec = exec;

/**
 * Run specified action. This is not the common method to call actions, but it's necessary for managing actions from
 * devices. On JS side, call actions directly
 * @param {type} type - Type of action to call
 * @param {data} type - Data to pass to action
 */
export var run = run;
<|endoftext|>


<commit_before>$( document ).ready(function() {
	    //Attaching Event to get the list of revisions
	    $('#page_button').click(function(){
	        //e.preventDefault();
		page = $('#page_name').val();
		rev = $('#page_rev').val();
		get_revisions(page,rev);
	    });
});
	function get_revisions(page,rev){
	    $.getJSON('http://en.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=ids&rvdir=newer&callback=?',{'titles':page,'rvlimit':rev},
	      function(data){
		//console.log(data);
		result_key = Object.keys(data.query.pages);
		list_of_revisions=data.query.pages[result_key].revisions;
		//console.log(list_of_revisions)
		start_rev = list_of_revisions.shift().revid;
		end_rev = list_of_revisions.shift().revid;
		wiki_diff();
		});
    }
	function wiki_diff(){
	    //getting list of revisions
	    $.getJSON('http://en.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=content&rvexpandtemplates&rvparse&callback=?',{'revids':start_rev},
				  function(data_1){
					result_key_1 = Object.keys(data_1.query.pages);
					data_rev_1 = data_1.query.pages[result_key_1].revisions[0]['*'];
					$.getJSON('http://en.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=content&rvparse&rvexpandtemplates&callback=?',{'revids':end_rev},
					function(data_2){
					      result_key_2 = Object.keys(data_2.query.pages);
					      data_rev_2 = data_2.query.pages[result_key_2].revisions[0]['*'];
					      //html diff
					      modified_html = diff(data_rev_1,data_rev_2);
					      console.log(modified_html);
					      //show the modifictaions by scrolling into view
					      $('#wiki_body').html(modified_html);
					      modify_list = $.makeArray($('del,ins'));
					      animate_diff();
					      function animate_diff(){
					      setTimeout(function(){
							    if(modify_list.length>0){
								element = modify_list[0];
								console.log(element);
								element.scrollIntoView();
                                                                if ($(element).prop('tagName') == 'DEL'){
                                                                        $(element).fadeOut(500);
                                                                }
                                                                else{
                                                                        $(element).show(500,'linear');
                                                                }
								modify_list.shift();
								animate_diff();
							    }
							    else{
								if(list_of_revisions.length>0){
								    start_rev = end_rev;
								    end_rev = list_of_revisions.shift().revid;
								    setTimeout(wiki_diff,200);
								}
							    }
					      },1000);
					      }
                                               });
					});
		}

<commit_msg>Clean up<commit_after>$( document ).ready(function() {
	    //Attaching Event to get the list of revisions
	    $('#page_button').click(function(){
	        //e.preventDefault();
		page = $('#page_name').val();
		rev = $('#page_rev').val();
		get_revisions(page,rev);
	    });
});
	function get_revisions(page,rev){
	    $.getJSON('https://en.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=ids&rvdir=newer&callback=?',{'titles':page,'rvlimit':rev},
	      function(data){
		//console.log(data);
		result_key = Object.keys(data.query.pages);
		list_of_revisions=data.query.pages[result_key].revisions;
		//console.log(list_of_revisions)
		start_rev = list_of_revisions.shift().revid;
		end_rev = list_of_revisions.shift().revid;
		wiki_diff();
		});
    }
	function wiki_diff(){
	    //getting list of revisions
	    $.getJSON('https://en.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=content&rvexpandtemplates&rvparse&callback=?',{'revids':start_rev},
				  function(data_1){
					result_key_1 = Object.keys(data_1.query.pages);
					data_rev_1 = data_1.query.pages[result_key_1].revisions[0]['*'];
					$.getJSON('https://en.wikipedia.org/w/api.php?format=json&action=query&prop=revisions&rvprop=content&rvparse&rvexpandtemplates&callback=?',{'revids':end_rev},
					function(data_2){
					      result_key_2 = Object.keys(data_2.query.pages);
					      data_rev_2 = data_2.query.pages[result_key_2].revisions[0]['*'];
					      //html diff
					      modified_html = diff(data_rev_1,data_rev_2);
					      console.log(modified_html);
					      //show the modifictaions by scrolling into view
					      $('#wiki_body').html(modified_html);
					      modify_list = $.makeArray($('del,ins'));
					      animate_diff();
					      function animate_diff(){
					      setTimeout(function(){
							    if(modify_list.length>0){
								element = modify_list[0];
								console.log(element);
								element.scrollIntoView();
                                                                if ($(element).prop('tagName') == 'DEL'){
                                                                        $(element).fadeOut(500);
                                                                }
                                                                else{
                                                                        $(element).show(500,'linear');
                                                                }
								modify_list.shift();
								animate_diff();
							    }
							    else{
								if(list_of_revisions.length>0){
								    start_rev = end_rev;
								    end_rev = list_of_revisions.shift().revid;
								    setTimeout(wiki_diff,200);
								}
							    }
					      },1000);
					      }
                                               });
					});
		}

<|endoftext|>