<commit_before>/**
 * System for orchestrating the various sound commands.
 * An abstraction around the `howler.js` sound library.
 */
import { getSetupMusic, getSoundEffects, getVoiceOver } from '../../../app/schemas/models/selectors/cinematic'
import { SyncFunction, Sleep, SequentialCommands } from './commands/commands'

import store from 'app/core/store'
import { BACKGROUND_VOLUME } from './constants'

// Returns a voice over command from list of audio files
// Includes a way to stop audio playing if user skips ahead, or goes backwards.
const createVoiceOverCommand = (voiceOverObj) => {
  const soundIdPromise = store.dispatch('voiceOver/preload', voiceOverObj)
  const voiceOverPlayCommand = new SyncFunction(() =>
    store.dispatch('voiceOver/playVoiceOver', soundIdPromise)
  )

  voiceOverPlayCommand.undoCommandFactory = () => {
    return new SyncFunction(() => {
      store.dispatch('audio/fadeTrack', { to: 0, track: 'voiceOver', duration: 100 })
    })
  }

  return voiceOverPlayCommand
}

export class SoundSystem {
  constructor () {
    this.undoSystemState = {
      lastBackgroundMusic: null
    }
  }

  /**
   * System interface method that CinematicParser calls with
   * each shot.
   * @param {Shot} shot
   * @returns {AbstractCommand[]}
   */
  parseSetupShot (shot) {
    const commands = []
    const music = getSetupMusic(shot)
    if (music) {
      const lastBackgroundMusic = this.undoSystemState.lastBackgroundMusic
      this.undoSystemState.lastBackgroundMusic = _.cloneDeep(music)
      const musicCommand = new SyncFunction(async () => {
        await store.dispatch('audio/stopTrack', 'background')

        await store.dispatch('audio/playSound', {
          track: 'background',
          volume: BACKGROUND_VOLUME,
          src: Object.values(music.files).map(f => `/file/${f}`),
          loop: music.loop
        })
      })
      musicCommand.undoCommandFactory = () => {
        if (lastBackgroundMusic) {
          return new SyncFunction(async () => {
            await store.dispatch('audio/stopTrack', 'background')
            await store.dispatch('audio/playSound', {
              track: 'background',
              src: Object.values(lastBackgroundMusic.files).map(f => `/file/${f}`),
              loop: lastBackgroundMusic.loop
            })
          })
        } else {
          return new SyncFunction(() => store.dispatch('audio/stopTrack', 'background'))
        }
      }
      commands.push(musicCommand)
    }
    return commands
  }

  /**
   * System interface method that CinematicParser calls on each dialogNode
   * of the cinematic.
   * @param {DialogNode} dialogNode
   * @returns {AbstractCommand[]}
   */
  parseDialogNode (dialogNode) {
    const soundCommands = []

    const soundEffects = getSoundEffects(dialogNode) || []
    soundEffects.forEach(({ sound, triggerStart }) => {
      const soundIdPromise = store.dispatch('audio/playSound', {
        track: 'soundEffects',
        autoplay: false,
        src: Object.values(sound).map(f => `/file/${f}`)
      })

      soundCommands.push(new SequentialCommands([
        new Sleep(triggerStart),
        new SyncFunction(async () => {
          const soundId = await soundIdPromise
          store.dispatch('audio/playSound', soundId)
        })
      ]))
    })

    const voiceOver = getVoiceOver(dialogNode)
    if (voiceOver) {
      soundCommands.push(
        createVoiceOverCommand(voiceOver)
      )
    }

    return soundCommands
  }

  stopAllSounds () {
    store.dispatch('audio/stopAll', { unload: true })
  }
}
<commit_msg>Cut cinematic VO for silent dialogue nodes (#620)<commit_after>/**
 * System for orchestrating the various sound commands.
 * An abstraction around the `howler.js` sound library.
 */
import { getSetupMusic, getSoundEffects, getVoiceOver } from '../../../app/schemas/models/selectors/cinematic'
import { SyncFunction, Sleep, SequentialCommands } from './commands/commands'

import store from 'app/core/store'
import { BACKGROUND_VOLUME } from './constants'

// Returns a voice over command from list of audio files
// Includes a way to stop audio playing if user skips ahead, or goes backwards.
const createVoiceOverCommand = (voiceOverObj) => {
  const soundIdPromise = store.dispatch('voiceOver/preload', voiceOverObj)
  const voiceOverPlayCommand = new SyncFunction(() =>
    store.dispatch('voiceOver/playVoiceOver', soundIdPromise)
  )

  voiceOverPlayCommand.undoCommandFactory = () => {
    return new SyncFunction(() => {
      store.dispatch('audio/fadeTrack', { to: 0, track: 'voiceOver', duration: 100 })
    })
  }

  return voiceOverPlayCommand
}

export class SoundSystem {
  constructor () {
    this.undoSystemState = {
      lastBackgroundMusic: null
    }
  }

  /**
   * System interface method that CinematicParser calls with
   * each shot.
   * @param {Shot} shot
   * @returns {AbstractCommand[]}
   */
  parseSetupShot (shot) {
    const commands = []
    const music = getSetupMusic(shot)
    if (music) {
      const lastBackgroundMusic = this.undoSystemState.lastBackgroundMusic
      this.undoSystemState.lastBackgroundMusic = _.cloneDeep(music)
      const musicCommand = new SyncFunction(async () => {
        await store.dispatch('audio/stopTrack', 'background')

        await store.dispatch('audio/playSound', {
          track: 'background',
          volume: BACKGROUND_VOLUME,
          src: Object.values(music.files).map(f => `/file/${f}`),
          loop: music.loop
        })
      })
      musicCommand.undoCommandFactory = () => {
        if (lastBackgroundMusic) {
          return new SyncFunction(async () => {
            await store.dispatch('audio/stopTrack', 'background')
            await store.dispatch('audio/playSound', {
              track: 'background',
              src: Object.values(lastBackgroundMusic.files).map(f => `/file/${f}`),
              loop: lastBackgroundMusic.loop
            })
          })
        } else {
          return new SyncFunction(() => store.dispatch('audio/stopTrack', 'background'))
        }
      }
      commands.push(musicCommand)
    }
    return commands
  }

  /**
   * System interface method that CinematicParser calls on each dialogNode
   * of the cinematic.
   * @param {DialogNode} dialogNode
   * @returns {AbstractCommand[]}
   */
  parseDialogNode (dialogNode) {
    const soundCommands = []

    const soundEffects = getSoundEffects(dialogNode) || []
    soundEffects.forEach(({ sound, triggerStart }) => {
      const soundIdPromise = store.dispatch('audio/playSound', {
        track: 'soundEffects',
        autoplay: false,
        src: Object.values(sound).map(f => `/file/${f}`)
      })

      soundCommands.push(new SequentialCommands([
        new Sleep(triggerStart),
        new SyncFunction(async () => {
          const soundId = await soundIdPromise
          store.dispatch('audio/playSound', soundId)
        })
      ]))
    })

    const voiceOver = getVoiceOver(dialogNode)
    if (voiceOver) {
      soundCommands.push(
        createVoiceOverCommand(voiceOver)
      )
    } else {
      // If no voice over, cut any VO that may be playing.
      soundCommands.push(
        new SyncFunction(() => {
          store.dispatch('audio/fadeTrack', { to: 0, track: 'voiceOver', duration: 100 })
        })
      )
    }

    return soundCommands
  }

  stopAllSounds () {
    store.dispatch('audio/stopAll', { unload: true })
  }
}
<|endoftext|>


<commit_before>var imageAddr = "https://jdc20181.github.io/testpics/SpeedTestImage.jpg";
var downloadSize = 8793622; //bytes
function ShowProgressMessage(msg) {
    if (console) {
        if (typeof msg == "string") {
            console.log(msg);
        } else {
            for (var i = 0; i < msg.length; i++) {
                console.log(msg[i]);
            }
        }
    }

    var oProgress = document.getElementById("progress");
    if (oProgress) {
        var actualHTML = (typeof msg == "string") ? msg : msg.join("
");
        oProgress.innerHTML = actualHTML;
    }
}
function InitiateSpeedDetection() {
    ShowProgressMessage("Calculating Results...");
    window.setTimeout(MeasureConnectionSpeed, 1);
};
if (window.addEventListener) {
    window.addEventListener('load', InitiateSpeedDetection, false);
} else if (window.attachEvent) {
    window.attachEvent('onload', InitiateSpeedDetection);
}
function MeasureConnectionSpeed() {
    var startTime, endTime;
    var download = new Image();
    download.onload = function () {
        endTime = (new Date()).getTime();
        showResults();
    }

    download.onerror = function (err, msg) {
        ShowProgressMessage("Invalid image, or error downloading");
    }

    startTime = (new Date()).getTime();
    var cacheBuster = "?nnn=" + startTime;
    download.src = imageAddr + cacheBuster;

 function showResults() {
        var duration = (endTime - startTime) / 1000;
        var bitsLoaded = downloadSize * 8 ; /* Experment: Change Var from 8 to 12 */
        var speedBps = (bitsLoaded / duration);
        var speedKbps = (speedBps / 1024);
        var speedMbps = (speedKbps / 1024);
        ShowProgressMessage([
            speedMbps.toFixed(2) + " Mbps",
        ]);
}
}
<commit_msg>Update api131.js<commit_after>var imageAddr = "https://jdc20181.github.io/testpics/SpeedTestImage.jpg";
var downloadSize = 8793622; //bytes
function ShowProgressMessage(msg) {
    if (console) {
        if (typeof msg == "string") {
            console.log(msg);
        } else {
            for (var i = 0; i < msg.length; i++) {
                console.log(msg[i]);
            }
        }
    }

    var oProgress = document.getElementById("progress");
    if (oProgress) {
        var actualHTML = (typeof msg == "string") ? msg : msg.join("
");
        oProgress.innerHTML = actualHTML;
    }
}
function InitiateSpeedDetection() {
    ShowProgressMessage("Calculating Results...");
    window.setTimeout(MeasureConnectionSpeed, 3000);
};
if (window.addEventListener) {
    window.addEventListener('load', InitiateSpeedDetection, false);
} else if (window.attachEvent) {
    window.attachEvent('onload', InitiateSpeedDetection);
}
function MeasureConnectionSpeed() {
    var startTime, endTime;
    var download = new Image();
    download.onload = function () {
        endTime = (new Date()).getTime();
        showResults();
    }

    download.onerror = function (err, msg) {
        ShowProgressMessage("Invalid image, or error downloading");
    }

    startTime = (new Date()).getTime();
    var cacheBuster = "?nnn=" + startTime;
    download.src = imageAddr + cacheBuster;

 function showResults() {
        var duration = (endTime - startTime) / 1000;
        var bitsLoaded = downloadSize * 8 ; /* Experment: Change Var from 8 to 12 */
        var speedBps = (bitsLoaded / duration);
        var speedKbps = (speedBps / 1024);
        var speedMbps = (speedKbps / 1024);
        ShowProgressMessage([
            speedMbps.toFixed(2) + " Mbps",
        ]);
}
}
<|endoftext|>


<commit_before>/**
 * @author windy
 * @version 2.0
 * Created by windy on 2021/5/18
 */
BI.SelectPatchEditor = BI.inherit(BI.Widget, {

    props: {
        baseCls: "bi-patch-select-editor",
        height: 24,
    },

    render: function () {
        var self = this, o = this.options;

        var debounceInputChange = BI.debounce(BI.bind(this._dealChange, this), 300);

        return BI.extend({
            type: "bi.state_editor",
            ref: function (_ref) {
                self.editor = _ref;
            },
            hgap: o.hgap,
            vgap: o.vgap,
            lgap: o.lgap,
            rgap: o.rgap,
            tgap: o.tgap,
            bgap: o.bgap,
            height: o.height,
            watermark: o.watermark,
            allowBlank: true,
            value: o.value,
            defaultText: o.text,
            text: o.text,
            tipType: o.tipType,
            warningTitle: o.warningTitle,
            el: {
                type: 'bi.textarea_editor',
                scrolly: false,
                validationChecker: function () {
                    return true;
                },
                throttle: true,
            },
            listeners: [{
                eventName: BI.Controller.EVENT_CHANGE,
                action: function (type, v) {
                    if (BI.contains(v, "\n")) {
                        self._dealChange(type, v);
                    } else {
                        debounceInputChange(type, v);
                    }
                },
            }, {
                eventName: BI.Editor.EVENT_FOCUS,
                action: function () {
                    self.fireEvent(BI.SelectPatchEditor.EVENT_FOCUS, arguments);
                },
            }, {
                eventName: BI.Editor.EVENT_BLUR,
                action: function () {
                    self.fireEvent(BI.SelectPatchEditor.EVENT_BLUR, arguments);
                },
            }],
        }, o.el);
    },

    _dealChange: function (type, v) {
        var value = "";
        if (BI.isKey(v)) {
            value = this._formatText(v);
        }
        if (type === BI.Events.CHANGE) {
            this._setValue(value);
            if (this._trimValue(value) !== "") {
                if (!this._start || !BI.isKey(this._lastValue) || (this._pause === true && !/\u200b\s\u200b$/.test(this.getValue()))) {
                    this._start = true;
                    this._pause = false;
                    this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.STARTEDIT, this.getValue(), this);
                }
            }
            if (this._trimValue(this._lastValue) !== this._trimValue(value)) {
                this.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
            }
            if (BI.endWith(value, BI.BlankSplitChar)) {
                this._pause = true;
                this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.PAUSE, "", this);
            }
        }
        if (type === BI.Events.EMPTY || type === BI.Events.STOPEDIT) {
            this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY);
        }
        this._lastValue = value;
    },

    _trimValue: function (v) {
        return BI.trim(BI.replaceAll(v || "", BI.BlankSplitChar, ""));
    },

    _formatText: function (v) {
        return BI.replaceAll(v || "", "\n", BI.BlankSplitChar);
    },

    setWaterMark: function (v) {
        this.editor.setWaterMark(v);
    },

    doRedMark: function () {
        this.editor.doRedMark.apply(this.editor, arguments);
    },

    unRedMark: function () {
        this.editor.unRedMark.apply(this.editor, arguments);
    },

    doHighLight: function () {
        this.editor.doHighLight.apply(this.editor, arguments);
    },

    unHighLight: function () {
        this.text.unHighLight.apply(this.text, arguments);
    },

    focus: function () {
        this.editor.focus();
    },

    blur: function () {
        this.editor.blur();
    },

    _setValue: function (v) {
        this.editor.setValue(this._formatText(v));
    },

    _showInput: function () {
        this.editor.visible();
        this.text.invisible();
    },

    _showHint: function () {
        this.editor.invisible();
        this.text.visible();
    },

    isValid: function () {
        return this.editor.isValid();
    },

    setErrorText: function (text) {
        this.editor.setErrorText(text);
    },

    getErrorText: function () {
        return this.editor.getErrorText();
    },

    isEditing: function () {
        return this.editor.isEditing();
    },

    getLastValidValue: function () {
        return this.editor.getLastValidValue();
    },

    getLastChangedValue: function () {
        return this.editor.getLastChangedValue();
    },

    setValue: function (v) {
        this._setValue(v);
        this._lastValue = this._trimValue(v);
    },

    getValue: function () {
        return BI.trim(this.editor.getValue());
    },

    getState: function () {
        return this.editor.getState();
    },

    setState: function (v) {
        this.editor.setState(v);
    },

    setTipType: function (v) {
        this.editor.setTipType(v);
    },

    getText: function () {
        return this.editor.getText();
    },
});
BI.SelectPatchEditor.EVENT_CHANGE = "EVENT_CHANGE";
BI.SelectPatchEditor.EVENT_FOCUS = "EVENT_FOCUS";
BI.SelectPatchEditor.EVENT_BLUR = "EVENT_BLUR";

BI.shortcut("bi.select_patch_editor", BI.SelectPatchEditor);<commit_msg>无JIRA任务 测试下fineui同步dec-fineui<commit_after>/**
 * @author windy
 * @version 2.0
 * Created by windy on 2021/5/18
 */
BI.SelectPatchEditor = BI.inherit(BI.Widget, {

    props: {
        baseCls: "bi-patch-select-editor",
        height: 24,
    },

    render: function () {
        var self = this, o = this.options;

        var debounceInputChange = BI.debounce(BI.bind(this._dealChange, this), 300);

        return BI.extend({
            type: "bi.state_editor",
            ref: function (_ref) {
                self.editor = _ref;
            },
            hgap: o.hgap,
            vgap: o.vgap,
            lgap: o.lgap,
            rgap: o.rgap,
            tgap: o.tgap,
            bgap: o.bgap,
            height: o.height,
            watermark: o.watermark,
            allowBlank: true,
            value: o.value,
            defaultText: o.text,
            text: o.text,
            tipType: o.tipType,
            warningTitle: o.warningTitle,
            el: {
                type: 'bi.textarea_editor',
                scrolly: false,
                validationChecker: function () {
                    return true;
                },
                throttle: true,
            },
            listeners: [{
                eventName: BI.Controller.EVENT_CHANGE,
                action: function (type, v) {
                    if (BI.contains(v, "\n")) {
                        self._dealChange(type, v);
                    } else {
                        debounceInputChange(type, v);
                    }
                },
            }, {
                eventName: BI.Editor.EVENT_FOCUS,
                action: function () {
                    self.fireEvent(BI.SelectPatchEditor.EVENT_FOCUS, arguments);
                },
            }, {
                eventName: BI.Editor.EVENT_BLUR,
                action: function () {
                    self.fireEvent(BI.SelectPatchEditor.EVENT_BLUR, arguments);
                },
            }],
        }, o.el);
    },

    _dealChange: function (type, v) {
        var value = "";
        if (v !== this.editor.getValue()) {
            return;
        }
        if (BI.isKey(v)) {
            value = this._formatText(v);
        }
        if (type === BI.Events.CHANGE) {
            this._setValue(value);
            if (this._trimValue(value) !== "") {
                if (!this._start || !BI.isKey(this._lastValue) || (this._pause === true && this._trimValue(this._lastValue) !== this._trimValue(value))) {
                    this._start = true;
                    this._pause = false;
                    this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.STARTEDIT, this.getValue(), this);
                }
            }
            if (this._trimValue(this._lastValue) !== this._trimValue(value)) {
                this.fireEvent(BI.Controller.EVENT_CHANGE, arguments);
            }
            if (BI.endWith(value, BI.BlankSplitChar)) {
                this._pause = true;
                this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.PAUSE, "", this);
            }
        }
        if (type === BI.Events.EMPTY || type === BI.Events.STOPEDIT) {
            this.fireEvent(BI.Controller.EVENT_CHANGE, BI.Events.EMPTY);
        }
        this._lastValue = value;
    },

    _trimValue: function (v) {
        return BI.trim(BI.replaceAll(v || "", BI.BlankSplitChar, ""));
    },

    _formatText: function (v) {
        return BI.replaceAll(v || "", "\n", BI.BlankSplitChar);
    },

    setWaterMark: function (v) {
        this.editor.setWaterMark(v);
    },

    doRedMark: function () {
        this.editor.doRedMark.apply(this.editor, arguments);
    },

    unRedMark: function () {
        this.editor.unRedMark.apply(this.editor, arguments);
    },

    doHighLight: function () {
        this.editor.doHighLight.apply(this.editor, arguments);
    },

    unHighLight: function () {
        this.text.unHighLight.apply(this.text, arguments);
    },

    focus: function () {
        this.editor.focus();
    },

    blur: function () {
        this.editor.blur();
    },

    _setValue: function (v) {
        this.editor.setValue(this._formatText(v));
    },

    _showInput: function () {
        this.editor.visible();
        this.text.invisible();
    },

    _showHint: function () {
        this.editor.invisible();
        this.text.visible();
    },

    isValid: function () {
        return this.editor.isValid();
    },

    setErrorText: function (text) {
        this.editor.setErrorText(text);
    },

    getErrorText: function () {
        return this.editor.getErrorText();
    },

    isEditing: function () {
        return this.editor.isEditing();
    },

    getLastValidValue: function () {
        return this.editor.getLastValidValue();
    },

    getLastChangedValue: function () {
        return this.editor.getLastChangedValue();
    },

    setValue: function (v) {
        this._setValue(v);
        this._lastValue = this._trimValue(v);
    },

    getValue: function () {
        return BI.trim(this.editor.getValue());
    },

    getState: function () {
        return this.editor.getState();
    },

    setState: function (v) {
        this.editor.setState(v);
    },

    setTipType: function (v) {
        this.editor.setTipType(v);
    },

    getText: function () {
        return this.editor.getText();
    },
});
BI.SelectPatchEditor.EVENT_CHANGE = "EVENT_CHANGE";
BI.SelectPatchEditor.EVENT_FOCUS = "EVENT_FOCUS";
BI.SelectPatchEditor.EVENT_BLUR = "EVENT_BLUR";

BI.shortcut("bi.select_patch_editor", BI.SelectPatchEditor);<|endoftext|>


<commit_before>/*
*     (C) Copyright 2008 Telefonica Investigacion y Desarrollo
*     S.A.Unipersonal (Telefonica I+D)
*
*     This file is part of Morfeo EzWeb Platform.
*
*     Morfeo EzWeb Platform is free software: you can redistribute it and/or modify
*     it under the terms of the GNU Affero General Public License as published by
*     the Free Software Foundation, either version 3 of the License, or
*     (at your option) any later version.
*
*     Morfeo EzWeb Platform is distributed in the hope that it will be useful,
*     but WITHOUT ANY WARRANTY; without even the implied warranty of
*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*     GNU Affero General Public License for more details.
*
*     You should have received a copy of the GNU Affero General Public License
*     along with Morfeo EzWeb Platform.  If not, see <http://www.gnu.org/licenses/>.
*
*     Info about members and contributors of the MORFEO project
*     is available at
*
*     http://morfeo-project.org
 */

/**
 * Base class for managing window menus whose HTML code is in templates/index.html.
 */

function WindowMenu(title, extra_class) {
    // Allow hierarchy
    if (arguments.length == 0)
        return;

    this.childWindow = null;
    this.htmlElement = document.createElement('div');  // create the root HTML element
    Element.extend(this.htmlElement);
    this.htmlElement.className = "window_menu";
    if (extra_class != null) {
        this.htmlElement.addClassName(extra_class);
    }

    // Window Top
    var windowTop = document.createElement('div');
    windowTop.className = "window_top";
    this.htmlElement.appendChild(windowTop);

    this._closeListener = this._closeListener.bind(this);
    this.closeButton = new StyledElements.StyledButton({
        'class': "closebutton",
        'plain': true
    });
    this.closeButton.insertInto(windowTop);
    this.closeButton.addEventListener("click", this._closeListener);

    this.titleElement = document.createElement('div');
    Element.extend(this.titleElement);
    this.titleElement.className = "window_title";
    windowTop.appendChild(this.titleElement);

    var clearer = document.createElement('div');
    Element.extend(clearer);
    clearer.addClassName("floatclearer");
    windowTop.appendChild(clearer);

    // Window Content
    this.windowContent = document.createElement('div');
    Element.extend(this.windowContent);
    this.windowContent.className = "window_content";
    this.htmlElement.appendChild(this.windowContent);

    this.iconElement = document.createElement('div');
    Element.extend(this.iconElement);
    this.iconElement.className = "window-icon icon-size";
    this.windowContent.appendChild(this.iconElement);

    this.msgElement = document.createElement('div');
    Element.extend(this.msgElement);
    this.msgElement.className = "msg";
    this.windowContent.appendChild(this.msgElement);

    // Window Bottom
    this.windowBottom = document.createElement('div');
    Element.extend(this.windowBottom);
    this.windowBottom.className = "window_bottom";
    this.htmlElement.appendChild(this.windowBottom);

    // Initial title
    this.setTitle(title);
}

WindowMenu.prototype.setTitle = function setTitle(title) {
    this.titleElement.setTextContent(title);
};

/**
 * @private
 *
 * Click Listener for the close button.
 */
WindowMenu.prototype._closeListener = function(e) {
    this.hide();
}

/**
 * Updates the message displayed by this WindowMenu
 */
WindowMenu.prototype.setMsg = function (msg) {
    this.msgElement.setTextContent(msg);

    if (isElement(this.htmlElement.parentNode)) {
        this.calculatePosition();
    }
}

/**
 * @private
 *
 * Calculates a usable absolute position for the window
 */
WindowMenu.prototype.calculatePosition = function() {
    var coordenates = [];

    var windowHeight = BrowserUtilsFactory.getInstance().getHeight();
    var windowWidth = BrowserUtilsFactory.getInstance().getWidth();

    this.htmlElement.setStyle({'max-height' : 'none'});
    var menuHeight = this.htmlElement.getHeight();
    var menuWidth = this.htmlElement.getWidth();

    if (menuWidth > windowWidth/2) {
        menuWidth = windowWidth/2; //IE6 hack
        this.htmlElement.setStyle({'width': menuWidth+'px'});
    }

    coordenates[1] = windowHeight/2 - menuHeight/2;
    coordenates[0] = windowWidth/2 - menuWidth/2;

    if (windowHeight < menuHeight) {
        var windowStyle = document.defaultView.getComputedStyle(this.htmlElement, null);

        var padding;
        padding = windowStyle.getPropertyCSSValue("padding-top").
                  getFloatValue(CSSPrimitiveValue.CSS_PX);
        padding+= windowStyle.getPropertyCSSValue("padding-bottom").
                  getFloatValue(CSSPrimitiveValue.CSS_PX);

        this.htmlElement.setStyle({'max-height': windowHeight - padding + 'px',
                                   'top': '0px'});
    } else {
        this.htmlElement.style.top = coordenates[1]+"px";
    }
    this.htmlElement.style.left = coordenates[0]+"px";

    if (this.childWindow != null) {
        this.childWindow.calculatePosition();
    }
}

/**
 *
 */
WindowMenu.prototype.setHandler = function (handler) {
    this.operationHandler = handler;
}

/**
 * Makes this WindowMenu visible.
 *
 * @param parentWindow
 */
WindowMenu.prototype.show = function (parentWindow) {
    this._parentWindowMenu = parentWindow;

    if (this._parentWindowMenu != null) {
        this._parentWindowMenu._addChildWindow(this);
    } else {
        LayoutManagerFactory.getInstance()._showWindowMenu(this);
    }

    document.body.appendChild(this.htmlElement);
    this.calculatePosition();
    this.htmlElement.style.display = "block";
    this.setFocus();
}

/**
 * Makes this WindowMenu hidden.
 */
WindowMenu.prototype.hide = function () {
    if (!isElement(this.htmlElement.parentNode)) {
        // This windowmenu is currently hidden => Nothing to do
        return;
    }

    this.htmlElement.parentNode.removeChild(this.htmlElement);
    if (this.msgElement != null) {
        this.msgElement.update();
    }
    if (this.childWindow != null) {
        this.childWindow.hide();
    }

    if (this._parentWindowMenu != null) {
        this._parentWindowMenu._removeChildWindow(this);
        this._parentWindowMenu = null;
    } else {
        LayoutManagerFactory.getInstance().hideCover();
    }
}

WindowMenu.prototype._addChildWindow = function (windowMenu) {
    if (windowMenu !== this) {
        this.childWindow = windowMenu;
    } else {
        throw TypeError('Window menus cannot be its own child');
    }
};

WindowMenu.prototype._removeChildWindow = function (windowMenu) {
    if (this.childWindow === windowMenu) {
        this.childWindow = null;
    }
};

WindowMenu.prototype.setFocus = function () {
}

/**
 * Specific class representing alert dialogs
 */
function AlertWindowMenu () {
    WindowMenu.call(this, gettext('Warning'));

    // Warning icon
    this.iconElement.className += ' icon-warning';

    // Accept button
    this.acceptButton = document.createElement('button');

    Element.extend(this.acceptButton);
    this.acceptButton.appendChild(document.createTextNode(gettext('Yes')));
    this._acceptListener = this._acceptListener.bind(this);
    this.acceptButton.observe("click", this._acceptListener);
    this.windowBottom.appendChild(this.acceptButton);

    // Cancel button
    this.cancelButton = document.createElement('button');
    Element.extend(this.cancelButton);
    this.cancelButton.appendChild(document.createTextNode(gettext('No')));
    this.cancelButton.observe("click", this._closeListener);
    this.windowBottom.appendChild(this.cancelButton);

    this.acceptHandler = null;
    this.cancelHandler = null;
}
AlertWindowMenu.prototype = new WindowMenu();

AlertWindowMenu.prototype._acceptListener = function(e) {
    this.acceptHandler();
    this.hide();
}

AlertWindowMenu.prototype._closeListener = function(e) {
    WindowMenu.prototype._closeListener.call(this, e);
    if (this.cancelHandler) this.cancelHandler();
}

AlertWindowMenu.prototype.setHandler = function(acceptHandler, cancelHandler) {
    this.acceptHandler = acceptHandler;
    this.cancelHandler = cancelHandler;
}

AlertWindowMenu.prototype.setFocus = function() {
    this.acceptButton.focus();
}

/**
 * Specific class for platform preferences windows.
 *
 * @param manager
 *
 * @author jmostazo-upm
 */
function PreferencesWindowMenu(scope, manager) {
    WindowMenu.call(this, '');

    this.manager = manager;
    var table = manager.getPreferencesDef().getInterface();
    this.windowContent.insertBefore(table, this.msgElement);

    // Accept button
    this.acceptButton = new StyledElements.StyledButton({
        text: gettext('Save'),
        'class': 'btn-primary'
    });
    this._executeOperation = this._executeOperation.bind(this);
    this.acceptButton.addEventListener("click", this._executeOperation);
    this.acceptButton.insertInto(this.windowBottom);

    // Cancel button
    this.cancelButton = new StyledElements.StyledButton({
        text: gettext('Cancel')
    });

    Element.extend(this.cancelButton);
    this.cancelButton.addEventListener("click", this._closeListener);
    this.cancelButton.insertInto(this.windowBottom);
}
PreferencesWindowMenu.prototype = new WindowMenu();

PreferencesWindowMenu.prototype.setCancelable = function(cancelable) {
    this.closeButton.setDisabled(!cancelable);
    if (cancelable === true) {
        this.cancelButton.enable();
    } else {
        this.cancelButton.disable();
    }
};

PreferencesWindowMenu.prototype._executeOperation = function() {
    // Validate input fields
    var validationManager = new ValidationErrorManager();
    for (var fieldId in this.fields)
        validationManager.validate(this.fields[fieldId].inputInterface);

    // Build Error Message
    var errorMsg = validationManager.toHTML();

    // Show error message if needed
    if (errorMsg != "") {
        this.setMsg(errorMsg);
    } else {
        this.manager.save();
        this.hide();
    }
}

PreferencesWindowMenu.prototype.show = function (parentWindow) {
    this.setTitle(this.manager.buildTitle());
    this.manager.resetInterface('platform');
    WindowMenu.prototype.show.call(this, parentWindow);
}
<commit_msg>Move iconElement to AlertWindowMenu<commit_after>/*
*     (C) Copyright 2008 Telefonica Investigacion y Desarrollo
*     S.A.Unipersonal (Telefonica I+D)
*
*     This file is part of Morfeo EzWeb Platform.
*
*     Morfeo EzWeb Platform is free software: you can redistribute it and/or modify
*     it under the terms of the GNU Affero General Public License as published by
*     the Free Software Foundation, either version 3 of the License, or
*     (at your option) any later version.
*
*     Morfeo EzWeb Platform is distributed in the hope that it will be useful,
*     but WITHOUT ANY WARRANTY; without even the implied warranty of
*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*     GNU Affero General Public License for more details.
*
*     You should have received a copy of the GNU Affero General Public License
*     along with Morfeo EzWeb Platform.  If not, see <http://www.gnu.org/licenses/>.
*
*     Info about members and contributors of the MORFEO project
*     is available at
*
*     http://morfeo-project.org
 */

/**
 * Base class for managing window menus whose HTML code is in templates/index.html.
 */

function WindowMenu(title, extra_class) {
    // Allow hierarchy
    if (arguments.length == 0)
        return;

    this.childWindow = null;
    this.htmlElement = document.createElement('div');  // create the root HTML element
    Element.extend(this.htmlElement);
    this.htmlElement.className = "window_menu";
    if (extra_class != null) {
        this.htmlElement.addClassName(extra_class);
    }

    // Window Top
    var windowTop = document.createElement('div');
    windowTop.className = "window_top";
    this.htmlElement.appendChild(windowTop);

    this._closeListener = this._closeListener.bind(this);
    this.closeButton = new StyledElements.StyledButton({
        'class': "closebutton",
        'plain': true
    });
    this.closeButton.insertInto(windowTop);
    this.closeButton.addEventListener("click", this._closeListener);

    this.titleElement = document.createElement('div');
    Element.extend(this.titleElement);
    this.titleElement.className = "window_title";
    windowTop.appendChild(this.titleElement);

    var clearer = document.createElement('div');
    Element.extend(clearer);
    clearer.addClassName("floatclearer");
    windowTop.appendChild(clearer);

    // Window Content
    this.windowContent = document.createElement('div');
    Element.extend(this.windowContent);
    this.windowContent.className = "window_content";
    this.htmlElement.appendChild(this.windowContent);

    this.msgElement = document.createElement('div');
    Element.extend(this.msgElement);
    this.msgElement.className = "msg";
    this.windowContent.appendChild(this.msgElement);

    // Window Bottom
    this.windowBottom = document.createElement('div');
    Element.extend(this.windowBottom);
    this.windowBottom.className = "window_bottom";
    this.htmlElement.appendChild(this.windowBottom);

    // Initial title
    this.setTitle(title);
}

WindowMenu.prototype.setTitle = function setTitle(title) {
    this.titleElement.setTextContent(title);
};

/**
 * @private
 *
 * Click Listener for the close button.
 */
WindowMenu.prototype._closeListener = function(e) {
    this.hide();
}

/**
 * Updates the message displayed by this WindowMenu
 */
WindowMenu.prototype.setMsg = function (msg) {
    this.msgElement.setTextContent(msg);

    if (isElement(this.htmlElement.parentNode)) {
        this.calculatePosition();
    }
}

/**
 * @private
 *
 * Calculates a usable absolute position for the window
 */
WindowMenu.prototype.calculatePosition = function() {
    var coordenates = [];

    var windowHeight = BrowserUtilsFactory.getInstance().getHeight();
    var windowWidth = BrowserUtilsFactory.getInstance().getWidth();

    this.htmlElement.setStyle({'max-height' : 'none'});
    var menuHeight = this.htmlElement.getHeight();
    var menuWidth = this.htmlElement.getWidth();

    if (menuWidth > windowWidth/2) {
        menuWidth = windowWidth/2; //IE6 hack
        this.htmlElement.setStyle({'width': menuWidth+'px'});
    }

    coordenates[1] = windowHeight/2 - menuHeight/2;
    coordenates[0] = windowWidth/2 - menuWidth/2;

    if (windowHeight < menuHeight) {
        var windowStyle = document.defaultView.getComputedStyle(this.htmlElement, null);

        var padding;
        padding = windowStyle.getPropertyCSSValue("padding-top").
                  getFloatValue(CSSPrimitiveValue.CSS_PX);
        padding+= windowStyle.getPropertyCSSValue("padding-bottom").
                  getFloatValue(CSSPrimitiveValue.CSS_PX);

        this.htmlElement.setStyle({'max-height': windowHeight - padding + 'px',
                                   'top': '0px'});
    } else {
        this.htmlElement.style.top = coordenates[1]+"px";
    }
    this.htmlElement.style.left = coordenates[0]+"px";

    if (this.childWindow != null) {
        this.childWindow.calculatePosition();
    }
}

/**
 *
 */
WindowMenu.prototype.setHandler = function (handler) {
    this.operationHandler = handler;
}

/**
 * Makes this WindowMenu visible.
 *
 * @param parentWindow
 */
WindowMenu.prototype.show = function (parentWindow) {
    this._parentWindowMenu = parentWindow;

    if (this._parentWindowMenu != null) {
        this._parentWindowMenu._addChildWindow(this);
    } else {
        LayoutManagerFactory.getInstance()._showWindowMenu(this);
    }

    document.body.appendChild(this.htmlElement);
    this.calculatePosition();
    this.htmlElement.style.display = "block";
    this.setFocus();
}

/**
 * Makes this WindowMenu hidden.
 */
WindowMenu.prototype.hide = function () {
    if (!isElement(this.htmlElement.parentNode)) {
        // This windowmenu is currently hidden => Nothing to do
        return;
    }

    this.htmlElement.parentNode.removeChild(this.htmlElement);
    if (this.msgElement != null) {
        this.msgElement.update();
    }
    if (this.childWindow != null) {
        this.childWindow.hide();
    }

    if (this._parentWindowMenu != null) {
        this._parentWindowMenu._removeChildWindow(this);
        this._parentWindowMenu = null;
    } else {
        LayoutManagerFactory.getInstance().hideCover();
    }
}

WindowMenu.prototype._addChildWindow = function (windowMenu) {
    if (windowMenu !== this) {
        this.childWindow = windowMenu;
    } else {
        throw TypeError('Window menus cannot be its own child');
    }
};

WindowMenu.prototype._removeChildWindow = function (windowMenu) {
    if (this.childWindow === windowMenu) {
        this.childWindow = null;
    }
};

WindowMenu.prototype.setFocus = function () {
}

/**
 * Specific class representing alert dialogs
 */
function AlertWindowMenu () {
    WindowMenu.call(this, gettext('Warning'));

    // Warning icon
    this.iconElement = document.createElement('div');
    Element.extend(this.iconElement);
    this.iconElement.className = "icon-size icon-warning";
    this.windowContent.insertBefore(this.iconElement, this.windowContent.childNodes[0]);

    // Accept button
    this.acceptButton = document.createElement('button');

    Element.extend(this.acceptButton);
    this.acceptButton.appendChild(document.createTextNode(gettext('Yes')));
    this._acceptListener = this._acceptListener.bind(this);
    this.acceptButton.observe("click", this._acceptListener);
    this.windowBottom.appendChild(this.acceptButton);

    // Cancel button
    this.cancelButton = document.createElement('button');
    Element.extend(this.cancelButton);
    this.cancelButton.appendChild(document.createTextNode(gettext('No')));
    this.cancelButton.observe("click", this._closeListener);
    this.windowBottom.appendChild(this.cancelButton);

    this.acceptHandler = null;
    this.cancelHandler = null;
}
AlertWindowMenu.prototype = new WindowMenu();

AlertWindowMenu.prototype._acceptListener = function(e) {
    this.acceptHandler();
    this.hide();
}

AlertWindowMenu.prototype._closeListener = function(e) {
    WindowMenu.prototype._closeListener.call(this, e);
    if (this.cancelHandler) this.cancelHandler();
}

AlertWindowMenu.prototype.setHandler = function(acceptHandler, cancelHandler) {
    this.acceptHandler = acceptHandler;
    this.cancelHandler = cancelHandler;
}

AlertWindowMenu.prototype.setFocus = function() {
    this.acceptButton.focus();
}

/**
 * Specific class for platform preferences windows.
 *
 * @param manager
 *
 * @author jmostazo-upm
 */
function PreferencesWindowMenu(scope, manager) {
    WindowMenu.call(this, '');

    this.manager = manager;
    var table = manager.getPreferencesDef().getInterface();
    this.windowContent.insertBefore(table, this.msgElement);

    // Accept button
    this.acceptButton = new StyledElements.StyledButton({
        text: gettext('Save'),
        'class': 'btn-primary'
    });
    this._executeOperation = this._executeOperation.bind(this);
    this.acceptButton.addEventListener("click", this._executeOperation);
    this.acceptButton.insertInto(this.windowBottom);

    // Cancel button
    this.cancelButton = new StyledElements.StyledButton({
        text: gettext('Cancel')
    });

    Element.extend(this.cancelButton);
    this.cancelButton.addEventListener("click", this._closeListener);
    this.cancelButton.insertInto(this.windowBottom);
}
PreferencesWindowMenu.prototype = new WindowMenu();

PreferencesWindowMenu.prototype.setCancelable = function(cancelable) {
    this.closeButton.setDisabled(!cancelable);
    if (cancelable === true) {
        this.cancelButton.enable();
    } else {
        this.cancelButton.disable();
    }
};

PreferencesWindowMenu.prototype._executeOperation = function() {
    // Validate input fields
    var validationManager = new ValidationErrorManager();
    for (var fieldId in this.fields)
        validationManager.validate(this.fields[fieldId].inputInterface);

    // Build Error Message
    var errorMsg = validationManager.toHTML();

    // Show error message if needed
    if (errorMsg != "") {
        this.setMsg(errorMsg);
    } else {
        this.manager.save();
        this.hide();
    }
}

PreferencesWindowMenu.prototype.show = function (parentWindow) {
    this.setTitle(this.manager.buildTitle());
    this.manager.resetInterface('platform');
    WindowMenu.prototype.show.call(this, parentWindow);
}
<|endoftext|>


<commit_before>define(function(require) {

  var RevenueByCategoryView = require('./RevenueByCategoryView');

  /**
   * Renders a smaller pie chart showing revenue
   * breakdown by category
   * @class app.ui.views.SmallRevenueByCategoryView
   * @extends app.ui.views.RevenueByCategoryView
   */

  var SmallRevenueByCategoryView = RevenueByCategoryView.extend({

    createChart: function() {
      var chart = RevenueByCategoryView.prototype.createChart.apply(this,arguments);
      chart.arcRadius(28);
      return chart;
    }

  });

  return SmallRevenueByCategoryView;

});<commit_msg>Removed labels from small pie chart<commit_after>define(function(require) {

  var RevenueByCategoryView = require('./RevenueByCategoryView');

  /**
   * Renders a smaller pie chart showing revenue
   * breakdown by category
   * @class app.ui.views.SmallRevenueByCategoryView
   * @extends app.ui.views.RevenueByCategoryView
   */

  var SmallRevenueByCategoryView = RevenueByCategoryView.extend({

    createChart: function() {
      var chart = RevenueByCategoryView.prototype.createChart.apply(this,arguments);
      chart
        .arcRadius(28)
        .showLabels(false);
      return chart;
    }

  });

  return SmallRevenueByCategoryView;

});<|endoftext|>


<commit_before>// Public Class Aria2 ( options )
var Aria2 = (function (_merge, _format, _isFunction) {
	var jsonrpc_ver = '2.0';

	// I'm lazy
	if (typeof GM_xmlhttpRequest == 'undefined') {
		console.error ('GM_xmlhttpRequest is required for `Aria2.jsonrpc` to work.');
		return null;
	}

	var AriaBase = function ( options ) {
		this.options = _merge ({
			auth: {
				type: AriaBase.AUTH.noAuth,
				user: '',
				pass: ''
			},
			host: '127.0.0.1',
			port: 6800
		}, options || {});

		this.id = parseInt (options, 10) || (0 + new Date());

		// 添加各类函数
		var that = this;
		[
			"addUri", "addTorrent", "addMetalink", "remove", "forceRemove",
			"pause", "pauseAll", "forcePause", "forcePauseAll", "unpause",
			"unpauseAll", "tellStatus", "getUris", "getFiles", "getPeers",
			"getServers", "tellActive", "tellWaiting", "tellStopped",
			"changePosition", "changeUri", "getOption", "changeOption",
			"getGlobalOption", "changeGlobalOption", "getGlobalStat",
			"purgeDownloadResult", "removeDownloadResult", "getVersion",
			"getSessionInfo", "shutdown", "forceShutdown", "saveSession"
		].forEach (function (sMethod) {
			that[sMethod] = function ( /* arg1, arg2, ... , [cbSuccess, [cbError]] */ ) {
				var args = [].slice.call (arguments);

				var cbSuccess, cbError;
				if (args.length && _isFunction(args[args.length - 1])) {
					cbSuccess = args[args.length - 1];
					args.splice (-1, 1);

					if (args.length && _isFunction(args[args.length - 1])) {
						cbError = cbSuccess;
						cbSuccess =args[args.length - 1];
						args.splice (-1, 1);
					}
				}

				return that.send ({
					method: 'aria2.' + sMethod,
					params: args
				}, cbSuccess, cbError);
			};
		});
	};

	// 静态常量
	AriaBase.AUTH = {
		noAuth: 0,
		user:   1,
		secret: 2
	};

	// public 函数
	AriaBase.prototype = {
		getAuthString: function () {
			var user = this.options.auth.user,
				pass = this.options.auth.pass;

			switch ( this.options.auth.type ) {
				case AriaBase.AUTH.noAuth:
					return '';

				case AriaBase.AUTH.user:
					break;

				case AriaBase.AUTH.secret:
					user = 'token';
					break;

				default:
					throw new Error('Undefined auth type: %s', this.options.auth.type);
					break;
			}

			return btoa (_format('%s:%s', user, pass));
		},

		send: function ( data, cbSuccess, cbError ) {
			var payload = {

				method: 'POST',
				url: _format('http://%s:%s/jsonrpc', this.options.host, this.options.port),
				headers: {
					'Content-Type': 'application/json; charset=UTF-8'
				},
				data: JSON.stringify ( _merge ({ jsonrpc: jsonrpc_ver, id: this.id }, data) ),
				onload: function (r) {
					if (cbSuccess)
						cbSuccess (JSON.parse (r.responseText));
				},
				onerror: cbError
			};

			if (this.options.auth.type !== AriaBase.AUTH.noAuth) {
				payload.headers.Authorization = 'Basic ' + this.getAuthString();
			}

			return GM_xmlhttpRequest (payload);
		}
	};

	return AriaBase;
})
// private 函数
((function (base) {
	var _isObject = function (obj) {
		return obj instanceof Object;
	};
	var _merge = function (base) {
		var args = arguments,
			argL = args.length;
		for ( var i = 1; i < argL; i++ ) {
			Object.keys (args[i]).forEach (function (key) {
				if (_isObject(args[i][key]) && _isObject(base[key])) {
					base[key] = _merge (base[key], args[i][key]);
				} else {
					base[key] = args[i][key];
				}
			});
		}
		return base;
	};
	return _merge;
})(), function (src) {
	var args = arguments,
		argL = args.length;

	var ret = src.slice ();
	for ( var i = 1; i < argL; i++ )
		ret = ret.replace ('%s', args[i]);
	return ret;
}, function (foo) {
	return typeof foo === 'function'
});<commit_msg>Init extra functions to prototype.<commit_after>// Public Class Aria2 ( options )
var Aria2 = (function (_merge, _format, _isFunction) {
	var jsonrpc_ver = '2.0';

	// I'm lazy
	if (typeof GM_xmlhttpRequest == 'undefined') {
		console.error ('GM_xmlhttpRequest is required for `Aria2.jsonrpc` to work.');
		return null;
	}

	var AriaBase = function ( options ) {
		this.options = _merge ({
			auth: {
				type: AriaBase.AUTH.noAuth,
				user: '',
				pass: ''
			},
			host: '127.0.0.1',
			port: 6800
		}, options || {});

		this.id = parseInt (options, 10) || (0 + new Date());
	};

	// 静态常量
	AriaBase.AUTH = {
		noAuth: 0,
		user:   1,
		secret: 2
	};

	// public 函数
	AriaBase.prototype = {
		getAuthString: function () {
			var user = this.options.auth.user,
				pass = this.options.auth.pass;

			switch ( this.options.auth.type ) {
				case AriaBase.AUTH.noAuth:
					return '';

				case AriaBase.AUTH.user:
					break;

				case AriaBase.AUTH.secret:
					user = 'token';
					break;

				default:
					throw new Error('Undefined auth type: %s', this.options.auth.type);
					break;
			}

			return btoa (_format('%s:%s', user, pass));
		},

		send: function ( data, cbSuccess, cbError ) {
			var payload = {

				method: 'POST',
				url: _format('http://%s:%s/jsonrpc', this.options.host, this.options.port),
				headers: {
					'Content-Type': 'application/json; charset=UTF-8'
				},
				data: JSON.stringify ( _merge ({ jsonrpc: jsonrpc_ver, id: this.id }, data) ),
				onload: function (r) {
					if (cbSuccess)
						cbSuccess (JSON.parse (r.responseText));
				},
				onerror: cbError
			};

			if (this.options.auth.type !== AriaBase.AUTH.noAuth) {
				payload.headers.Authorization = 'Basic ' + this.getAuthString();
			}

			return GM_xmlhttpRequest (payload);
		}
	};

	// 添加各类函数
	var that = this;
	[
		"addUri", "addTorrent", "addMetalink", "remove", "forceRemove",
		"pause", "pauseAll", "forcePause", "forcePauseAll", "unpause",
		"unpauseAll", "tellStatus", "getUris", "getFiles", "getPeers",
		"getServers", "tellActive", "tellWaiting", "tellStopped",
		"changePosition", "changeUri", "getOption", "changeOption",
		"getGlobalOption", "changeGlobalOption", "getGlobalStat",
		"purgeDownloadResult", "removeDownloadResult", "getVersion",
		"getSessionInfo", "shutdown", "forceShutdown", "saveSession"
	].forEach (function (sMethod) {
		// arg1, arg2, ... , [cbSuccess, [cbError]]
		AriaBase.prototype[sMethod] = function ( ) {
			var args = [].slice.call (arguments);

			var cbSuccess, cbError;
			if (args.length && _isFunction(args[args.length - 1])) {
				cbSuccess = args[args.length - 1];
				args.splice (-1, 1);

				if (args.length && _isFunction(args[args.length - 1])) {
					cbError = cbSuccess;
					cbSuccess =args[args.length - 1];
					args.splice (-1, 1);
				}
			}

			return this.send ({
				method: 'aria2.' + sMethod,
				params: args
			}, cbSuccess, cbError);
		};
	});

	return AriaBase;
})
// private 函数
((function (base) {
	var _isObject = function (obj) {
		return obj instanceof Object;
	};
	var _merge = function (base) {
		var args = arguments,
			argL = args.length;
		for ( var i = 1; i < argL; i++ ) {
			Object.keys (args[i]).forEach (function (key) {
				if (_isObject(args[i][key]) && _isObject(base[key])) {
					base[key] = _merge (base[key], args[i][key]);
				} else {
					base[key] = args[i][key];
				}
			});
		}
		return base;
	};
	return _merge;
})(), function (src) {
	var args = arguments,
		argL = args.length;

	var ret = src.slice ();
	for ( var i = 1; i < argL; i++ )
		ret = ret.replace ('%s', args[i]);
	return ret;
}, function (foo) {
	return typeof foo === 'function'
});<|endoftext|>


<commit_before>'use strict';

var oo = require('substance/util/oo');
var Panel = require('substance/ui/Panel');
var Component = require('substance/ui/Component');
var BibItemComponent = require('./BibItemComponent');
var $$ = Component.$$;

function BibliographySummary() {
  BibliographySummary.super.apply(this, arguments);
}

Component.extend(BibliographySummary, function BibliographySummaryProto() {
  this.handleAddBibItems = function(e) {
    e.preventDefault();
    this.send('switchContext', 'add-bib-items');
  };

  this.render = function() {
    var el = $$('div').addClass('se-bibliography-summary');

    el.append(
      $$('p').append(
        'Your bibliography has ',
        $$('strong').append(this.props.bibItems.length.toString(), ' references'),
        ' in total.'
        // $$('strong').append('? references'),
        // 'are not cited.'
      )
    );

    var config = this.context.config;
    if (config.isEditable) {
      el.append(
        $$('p').append(
          $$('a').attr({href: '#'})
            .on('click', this.handleAddBibItems)
            .append('Add references')
        )
      );
    }
    return el;
  };
});

// List existing bib items
// -----------------

function BibItemsPanel() {
  Panel.apply(this, arguments);

  var doc = this.props.doc;
  this.bibliography = doc.getCollection('bib-item');
  this.bibliography.connect(this, {
    'bibliography:updated': this.rerender
  });
}

BibItemsPanel.Prototype = function() {

  this.dispose = function() {
    this.bibliography.disconnect();
  };

  this.didMount = function() {
    var bibItemId = this.getFirstActiveBibItemId();
    if (bibItemId) {
      this.scrollToNode(bibItemId);
    }
  };

  this.getFirstActiveBibItemId = function() {
    var doc = this.props.doc;
    if (this.props.bibItemId) {
      return this.props.bibItemId;
    }

    if (this.props.citationId) {
      var citation = doc.get(this.props.citationId);
      return citation.targets[0];
    }
  };

  this.isHighlighted = function(bibItem) {
    var doc = this.props.doc;
    if (this.props.bibItemId === bibItem.id) {
      return true;
    }

    if (this.props.citationId) {
      var citation = doc.get(this.props.citationId);
      if (citation.targets && citation.targets.indexOf(bibItem.id) >= 0) {
        return true;
      }
    }
    return false;
  };

  this.render = function() {
    var bibItems = this.bibliography.getItems();
    var el = $$('div').addClass('sc-bib-items-panel sc-panel');
    var bibItemEls = $$('div').addClass('se-panel-content').ref('panelContent');

    bibItemEls.append($$(BibliographySummary, {bibItems: bibItems}));

    bibItems.forEach(function(bibItem) {
      bibItemEls.append($$(BibItemComponent, {
        node: bibItem,
        toggleName: this.i18n.t('focus'),
        highlighted: this.isHighlighted(bibItem)
      }));
    }.bind(this));
    el.append(bibItemEls);
    return el;
  };

  // this.handleDeleteBibItem = function(e) {
  //   e.preventDefault();
  //   var bibItemId = e.currentTarget.dataset.id;
  //   var doc = this.props.doc;

  //   doc.deleteBibItem(bibItemId);
  //   var bibliography = doc.getBibliography();
  //   // Recompile bibliography
  //   bibliography.compile();
  //   var bibItems = bibliography.getCompiledItems();
  //   this.setState({
  //     bibItems: bibItems
  //   });
  // };
};

oo.inherit(BibItemsPanel, Panel);

module.exports = BibItemsPanel;
<commit_msg>Fix BibItemsPanel.dispose.<commit_after>'use strict';

var oo = require('substance/util/oo');
var Panel = require('substance/ui/Panel');
var Component = require('substance/ui/Component');
var BibItemComponent = require('./BibItemComponent');
var $$ = Component.$$;

function BibliographySummary() {
  BibliographySummary.super.apply(this, arguments);
}

Component.extend(BibliographySummary, function BibliographySummaryProto() {
  this.handleAddBibItems = function(e) {
    e.preventDefault();
    this.send('switchContext', 'add-bib-items');
  };

  this.render = function() {
    var el = $$('div').addClass('se-bibliography-summary');

    el.append(
      $$('p').append(
        'Your bibliography has ',
        $$('strong').append(this.props.bibItems.length.toString(), ' references'),
        ' in total.'
        // $$('strong').append('? references'),
        // 'are not cited.'
      )
    );

    var config = this.context.config;
    if (config.isEditable) {
      el.append(
        $$('p').append(
          $$('a').attr({href: '#'})
            .on('click', this.handleAddBibItems)
            .append('Add references')
        )
      );
    }
    return el;
  };
});

// List existing bib items
// -----------------

function BibItemsPanel() {
  Panel.apply(this, arguments);

  var doc = this.props.doc;
  this.bibliography = doc.getCollection('bib-item');
  this.bibliography.connect(this, {
    'bibliography:updated': this.rerender
  });
}

BibItemsPanel.Prototype = function() {

  this.dispose = function() {
    this.bibliography.disconnect(this);
  };

  this.didMount = function() {
    var bibItemId = this.getFirstActiveBibItemId();
    if (bibItemId) {
      this.scrollToNode(bibItemId);
    }
  };

  this.getFirstActiveBibItemId = function() {
    var doc = this.props.doc;
    if (this.props.bibItemId) {
      return this.props.bibItemId;
    }

    if (this.props.citationId) {
      var citation = doc.get(this.props.citationId);
      return citation.targets[0];
    }
  };

  this.isHighlighted = function(bibItem) {
    var doc = this.props.doc;
    if (this.props.bibItemId === bibItem.id) {
      return true;
    }

    if (this.props.citationId) {
      var citation = doc.get(this.props.citationId);
      if (citation.targets && citation.targets.indexOf(bibItem.id) >= 0) {
        return true;
      }
    }
    return false;
  };

  this.render = function() {
    var bibItems = this.bibliography.getItems();
    var el = $$('div').addClass('sc-bib-items-panel sc-panel');
    var bibItemEls = $$('div').addClass('se-panel-content').ref('panelContent');

    bibItemEls.append($$(BibliographySummary, {bibItems: bibItems}));

    bibItems.forEach(function(bibItem) {
      bibItemEls.append($$(BibItemComponent, {
        node: bibItem,
        toggleName: this.i18n.t('focus'),
        highlighted: this.isHighlighted(bibItem)
      }));
    }.bind(this));
    el.append(bibItemEls);
    return el;
  };

  // this.handleDeleteBibItem = function(e) {
  //   e.preventDefault();
  //   var bibItemId = e.currentTarget.dataset.id;
  //   var doc = this.props.doc;

  //   doc.deleteBibItem(bibItemId);
  //   var bibliography = doc.getBibliography();
  //   // Recompile bibliography
  //   bibliography.compile();
  //   var bibItems = bibliography.getCompiledItems();
  //   this.setState({
  //     bibItems: bibItems
  //   });
  // };
};

oo.inherit(BibItemsPanel, Panel);

module.exports = BibItemsPanel;
<|endoftext|>


<commit_before>define(['jobCommChannel', 'base/js/namespace', 'common/runtime'], (
    JobCommChannel,
    Jupyter,
    Runtime
) => {
    'use strict';

    const DEFAULT_COMM_INFO = {
        content: {
            comms: [],
        },
    };
    const DEFAULT_COMM = {
        on_msg: () => {},
        send: () => {},
        send_shell_message: () => {},
    };

    function makeMockNotebook(commInfoReturn, registerTargetReturn, executeReply) {
        commInfoReturn = commInfoReturn || DEFAULT_COMM_INFO;
        registerTargetReturn = registerTargetReturn || DEFAULT_COMM;
        executeReply = executeReply || {};
        return {
            save_checkpoint: () => {
                /* no op */
            },
            kernel: {
                comm_info: (name, cb) => cb(commInfoReturn),
                execute: (code, cb) => cb.shell.reply({ content: executeReply }),
                comm_manager: {
                    register_comm: () => {},
                    register_target: (name, cb) => cb(registerTargetReturn, {}),
                },
            },
        };
    }

    function makeCommMsg(msgType, content) {
        return {
            content: {
                data: {
                    msg_type: msgType,
                    content: content,
                },
            },
        };
    }

    describe('The jobCommChannel widget', () => {
        let testBus;
        beforeEach(() => {
            testBus = Runtime.make().bus();
            Jupyter.notebook = makeMockNotebook();
        });

        afterEach(() => {
            window.kbaseRuntime = null;
            Jupyter.notebook = null;
            testBus = null;
        });

        it('Should load properly', () => {
            expect(JobCommChannel).not.toBeNull();
        });

        it('Should be instantiable and contain the right components', () => {
            const comm = new JobCommChannel();
            expect(comm.initCommChannel).toBeDefined();
            expect(comm.jobStates).toEqual({});
        });

        it('Should initialize correctly in the base case', () => {
            const comm = new JobCommChannel();
            return comm.initCommChannel().then(() => {
                expect(comm.comm).not.toBeNull();
            });
        });

        it('Should re-initialize with an existing channel', () => {
            const comm = new JobCommChannel();
            Jupyter.notebook = makeMockNotebook({
                content: {
                    comms: {
                        12345: {
                            target_name: 'KBaseJobs',
                        },
                    },
                },
            });
            return comm.initCommChannel().then(() => {
                expect(comm.comm).not.toBeNull();
            });
        });

        it('Should fail to initialize with a failed reply from the JobManager startup', async () => {
            const comm = new JobCommChannel();
            Jupyter.notebook = makeMockNotebook(null, null, {
                name: 'Failed to start',
                evalue: 'Some error',
                error: 'Yes. Very yes.',
            });
            await expectAsync(comm.initCommChannel()).toBeRejectedWith(
                new Error('Failed to start:Some error')
            );
        });

        const busMsgCases = [
            ['ping-comm-channel', { pingId: 'ping!' }],
            ['request-job-cancellation', { jobId: 'someJob' }],
            ['request-job-status', { jobId: 'someJob', parentJobId: 'someParent' }],
            ['request-job-update', { jobId: 'someJob', parentJobId: 'someParent' }],
            ['request-job-completion', { jobId: 'someJob' }],
            ['request-job-log', { jobId: 'someJob', options: {} }],
            ['request-latest-job-log', { jobId: 'someJob', options: {} }],
            ['request-job-info', { jobId: 'someJob', parentJobId: 'someParent' }],
        ];
        busMsgCases.forEach((testCase) => {
            it('Should handle ' + testCase[0] + ' bus message', () => {
                const comm = new JobCommChannel();
                return comm
                    .initCommChannel()
                    .then(() => {
                        expect(comm.comm).not.toBeNull();
                        spyOn(comm.comm, 'send');
                        testBus.emit(testCase[0], testCase[1]);
                        return new Promise((resolve) => setTimeout(resolve, 4000));
                    })
                    .then(() => {
                        expect(comm.comm.send).toHaveBeenCalled();
                    });
            });
        });

        it('Should error properly when trying to send a comm with an uninited channel', async () => {
            const comm = new JobCommChannel();
            await expectAsync(comm.sendCommMessage('some_msg', 'foo', {})).toBeRejected();
        });

        /* Mocking out comm messages coming back over the channel is gruesome. Just
         * calling the handleCommMessage function directly.
         */
        it('Should handle a start message', () => {
            const comm = new JobCommChannel();
            spyOn(console, 'warn');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(makeCommMsg('start', {}));
                // this one's basically a no-op right now, just make sure that
                // console.warn wasn't called.
                expect(console.warn).not.toHaveBeenCalled();
            });
        });

        it('Should respond to new_job by saving the Narrative', () => {
            const comm = new JobCommChannel();
            spyOn(Jupyter.notebook, 'save_checkpoint');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(makeCommMsg('new_job', {}));
                expect(Jupyter.notebook.save_checkpoint).toHaveBeenCalled();
            });
        });

        it('Should send job_status messages to the bus', () => {
            const jobId = 'someJob',
                msg = makeCommMsg('job_status', {
                    state: {
                        job_id: jobId,
                    },
                    spec: {},
                    widget_info: {},
                }),
                busMsg = {
                    jobId: jobId,
                    jobState: {
                        job_id: jobId,
                    },
                    outputWidgetInfo: {},
                };

            const comm = new JobCommChannel();
            spyOn(testBus, 'send');

            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { jobId: jobId },
                    key: { type: 'job-status' },
                });
            });
        });

        it('Should send a set of job statuses to the bus, and delete extras', () => {
            const msg = makeCommMsg('job_status_all', {
                id1: {
                    state: {
                        job_id: 'id1',
                    },
                },
                id2: {
                    state: {
                        job_id: 'id2',
                    },
                },
            });
            const comm = new JobCommChannel();
            comm.jobStates['deletedJob'] = { state: { job_id: 'deletedJob' } };
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledTimes(3);
            });
        });

        it('Should send a job-info message to the bus', () => {
            const jobId = 'foo',
                jobStateMsg = {
                    job_id: jobId,
                    state: {
                        job_id: jobId,
                    },
                },
                busMsg = {
                    jobId: jobId,
                    jobInfo: jobStateMsg,
                },
                msg = makeCommMsg('job_info', jobStateMsg);

            const comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { jobId: jobId },
                    key: { type: 'job-info' },
                });
            });
        });

        it('Should send a run_status message to the bus', () => {
            const jobId = 'foo',
                cellId = 'bar',
                busMsg = {
                    cell_id: cellId,
                    job_id: jobId,
                },
                msg = makeCommMsg('run_status', busMsg);
            const comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { cell: cellId },
                    key: { type: 'run-status' },
                });
            });
        });

        it('Should send job_canceled message to the bus', () => {
            const jobId = 'foo-canceled',
                msg = makeCommMsg('job_canceled', {
                    job_id: jobId,
                });
            const comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        via: 'job_canceled',
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-canceled' },
                    }
                );
            });
        });

        it('Should send job_does_not_exist messages to the bus', () => {
            const jobId = 'foo-dne',
                msg = makeCommMsg('job_does_not_exist', {
                    job_id: jobId,
                    source: 'someSource',
                }),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        source: 'someSource',
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-does-not-exist' },
                    }
                );
            });
        });

        it('Should send job_logs to the bus', () => {
            const jobId = 'foo-logs',
                msg = makeCommMsg('job_logs', {
                    job_id: jobId,
                    latest: true,
                    logs: [{}],
                }),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        logs: msg.content.data.content,
                        latest: msg.content.data.content.latest,
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-logs' },
                    }
                );
            });
        });

        const errCases = {
            cancel_job: 'job-cancel-error',
            job_logs: 'job-log-deleted',
            job_logs_latest: 'job-log-deleted',
            job_status: 'job-status-error',
        };

        Object.keys(errCases).forEach((errCase) => {
            it('Should handle job_comm_error of type ' + errCase, () => {
                const jobId = 'job-' + errCase,
                    errMsg = errCase + ' error happened!',
                    msg = makeCommMsg('job_comm_error', {
                        source: errCase,
                        job_id: jobId,
                        message: errMsg,
                    }),
                    comm = new JobCommChannel();
                spyOn(testBus, 'send');
                return comm.initCommChannel().then(() => {
                    comm.handleCommMessages(msg);
                    expect(testBus.send).toHaveBeenCalledWith(
                        {
                            jobId: jobId,
                            message: errMsg,
                        },
                        {
                            channel: { jobId: jobId },
                            key: { type: errCases[errCase] },
                        }
                    );
                });
            });
        });

        it('Handle unknown job errors generically', () => {
            const jobId = 'jobWithErrors',
                errMsg = 'some random error',
                requestType = 'some-error',
                busMsg = {
                    source: requestType,
                    job_id: jobId,
                    message: errMsg,
                },
                msg = makeCommMsg('job_comm_error', busMsg),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        message: errMsg,
                        request: requestType,
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-error' },
                    }
                );
            });
        });

        ['job_init_err', 'job_init_lookup_err'].forEach((errType) => {
            it(`Should handle ${errType}`, () => {
                const msg = makeCommMsg(errType, {
                        service: 'job service',
                        error: 'An error happened!',
                        name: 'Error',
                        source: 'jobmanager',
                    }),
                    comm = new JobCommChannel();
                return comm
                    .initCommChannel()
                    .then(() => {
                        comm.handleCommMessages(msg);
                        return new Promise((resolve) => {
                            setTimeout(() => resolve(), 500);
                        });
                    })
                    .then(() => {
                        expect(document.querySelector('.modal #kb-job-err-trace')).not.toBeNull();
                        // click the 'OK' button
                        document
                            .querySelector('.modal a.btn.btn-default.kb-job-err-dialog__button')
                            .click();
                        // expect it to be gone
                        return new Promise((resolve) => {
                            setTimeout(() => resolve(), 500);
                        });
                    })
                    .then(() => {
                        expect(document.querySelector('.modal #kb-job-err-trace')).toBeNull();
                    })
                    .finally(() => {
                        document.body.innerHTML = '';
                    });
            });
        });

        it('Should send a result message to the bus', () => {
            const cellId = 'someCellId',
                busMsg = {
                    address: { cell_id: cellId },
                    result: [1],
                },
                msg = makeCommMsg('result', busMsg),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { cell: cellId },
                    key: { type: 'result' },
                });
            });
        });

        it('Should handle unknown messages with console warnings', () => {
            const comm = new JobCommChannel(),
                msg = makeCommMsg('unknown_weird_msg', {});
            return comm.initCommChannel().then(() => {
                spyOn(console, 'warn');
                comm.handleCommMessages(msg);
                expect(console.warn).toHaveBeenCalled();
            });
        });
    });
});
<commit_msg>use Promises to wait for jobs messages; prevent modals from hanging around in the DOM<commit_after>define(['jobCommChannel', 'base/js/namespace', 'common/runtime', 'testUtil'], (
    JobCommChannel,
    Jupyter,
    Runtime,
    TestUtil
) => {
    'use strict';

    const DEFAULT_COMM_INFO = {
        content: {
            comms: [],
        },
    };
    const DEFAULT_COMM = {
        on_msg: () => {},
        send: () => {},
        send_shell_message: () => {},
    };

    function makeMockNotebook(commInfoReturn, registerTargetReturn, executeReply) {
        commInfoReturn = commInfoReturn || DEFAULT_COMM_INFO;
        registerTargetReturn = registerTargetReturn || DEFAULT_COMM;
        executeReply = executeReply || {};
        return {
            save_checkpoint: () => {
                /* no op */
            },
            kernel: {
                comm_info: (name, cb) => cb(commInfoReturn),
                execute: (code, cb) => cb.shell.reply({ content: executeReply }),
                comm_manager: {
                    register_comm: () => {},
                    register_target: (name, cb) => cb(registerTargetReturn, {}),
                },
            },
        };
    }

    function makeCommMsg(msgType, content) {
        return {
            content: {
                data: {
                    msg_type: msgType,
                    content: content,
                },
            },
        };
    }

    describe('The jobCommChannel widget', () => {
        let testBus;
        beforeEach(() => {
            testBus = Runtime.make().bus();
            Jupyter.notebook = makeMockNotebook();
        });

        afterEach(() => {
            window.kbaseRuntime = null;
            Jupyter.notebook = null;
            testBus = null;
        });

        it('Should load properly', () => {
            expect(JobCommChannel).not.toBeNull();
        });

        it('Should be instantiable and contain the right components', () => {
            const comm = new JobCommChannel();
            expect(comm.initCommChannel).toBeDefined();
            expect(comm.jobStates).toEqual({});
        });

        it('Should initialize correctly in the base case', () => {
            const comm = new JobCommChannel();
            return comm.initCommChannel().then(() => {
                expect(comm.comm).not.toBeNull();
            });
        });

        it('Should re-initialize with an existing channel', () => {
            const comm = new JobCommChannel();
            Jupyter.notebook = makeMockNotebook({
                content: {
                    comms: {
                        12345: {
                            target_name: 'KBaseJobs',
                        },
                    },
                },
            });
            return comm.initCommChannel().then(() => {
                expect(comm.comm).not.toBeNull();
            });
        });

        it('Should fail to initialize with a failed reply from the JobManager startup', async () => {
            const comm = new JobCommChannel();
            Jupyter.notebook = makeMockNotebook(null, null, {
                name: 'Failed to start',
                evalue: 'Some error',
                error: 'Yes. Very yes.',
            });
            await expectAsync(comm.initCommChannel()).toBeRejectedWith(
                new Error('Failed to start:Some error')
            );
        });

        const busMsgCases = [
            { channel: 'ping-comm-channel', message: { pingId: 'ping!' }, type: 'ping' },
            {
                channel: 'request-job-cancellation',
                message: { jobId: 'someJob' },
                type: 'cancel_job',
            },
            {
                channel: 'request-job-status',
                message: { jobId: 'someJob', parentJobId: 'someParent' },
                type: 'job_status',
            },
            {
                channel: 'request-job-update',
                message: { jobId: 'someJob', parentJobId: 'someParent' },
                type: 'start_job_update',
            },
            {
                channel: 'request-job-completion',
                message: { jobId: 'someJob' },
                type: 'stop_job_update',
            },
            {
                channel: 'request-job-log',
                message: { jobId: 'someJob', options: {} },
                type: 'job_logs',
            },
            {
                channel: 'request-latest-job-log',
                message: { jobId: 'someJob', options: {} },
                type: 'job_logs_latest',
            },
            {
                channel: 'request-job-info',
                message: { jobId: 'someJob', parentJobId: 'someParent' },
                type: 'job_info',
            },
        ];
        busMsgCases.forEach((testCase) => {
            it('Should handle ' + testCase.channel + ' bus message', () => {
                const comm = new JobCommChannel();
                return comm
                    .initCommChannel()
                    .then(() => {
                        expect(comm.comm).not.toBeNull();
                        return new Promise((resolve) => {
                            // resolve the promise when we see comm.comm.send(...)
                            spyOn(comm.comm, 'send').and.callFake((...args) => {
                                resolve(args);
                            });
                            testBus.emit(testCase.channel, testCase.message);
                        });
                    })
                    .then((args) => {
                        expect(comm.comm.send).toHaveBeenCalled();
                        expect(args[0].target_name).toEqual('KBaseJobs');
                        expect(args[0].request_type).toEqual(testCase.type);
                        // expect the message values to exist in the comm channel message
                        // the `options` object gets merged into the message if it is not empty
                        // since all the examples have an empty options object, we can ignore it
                        Object.keys(testCase.message)
                            .filter((key) => key !== 'options')
                            .forEach((key) => {
                                expect(Object.values(args[0])).toContain(testCase.message[key]);
                            });
                    });
            });
        });

        it('Should error properly when trying to send a comm with an uninited channel', async () => {
            const comm = new JobCommChannel();
            await expectAsync(comm.sendCommMessage('some_msg', 'foo', {})).toBeRejected();
        });

        /* Mocking out comm messages coming back over the channel is gruesome. Just
         * calling the handleCommMessage function directly.
         */
        it('Should handle a start message', () => {
            const comm = new JobCommChannel();
            spyOn(console, 'warn');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(makeCommMsg('start', {}));
                // this one's basically a no-op right now, just make sure that
                // console.warn wasn't called.
                expect(console.warn).not.toHaveBeenCalled();
            });
        });

        it('Should respond to new_job by saving the Narrative', () => {
            const comm = new JobCommChannel();
            spyOn(Jupyter.notebook, 'save_checkpoint');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(makeCommMsg('new_job', {}));
                expect(Jupyter.notebook.save_checkpoint).toHaveBeenCalled();
            });
        });

        it('Should send job_status messages to the bus', () => {
            const jobId = 'someJob',
                msg = makeCommMsg('job_status', {
                    state: {
                        job_id: jobId,
                    },
                    spec: {},
                    widget_info: {},
                }),
                busMsg = {
                    jobId: jobId,
                    jobState: {
                        job_id: jobId,
                    },
                    outputWidgetInfo: {},
                };

            const comm = new JobCommChannel();
            spyOn(testBus, 'send');

            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { jobId: jobId },
                    key: { type: 'job-status' },
                });
            });
        });

        it('Should send a set of job statuses to the bus, and delete extras', () => {
            const msg = makeCommMsg('job_status_all', {
                id1: {
                    state: {
                        job_id: 'id1',
                    },
                },
                id2: {
                    state: {
                        job_id: 'id2',
                    },
                },
            });
            const comm = new JobCommChannel();
            comm.jobStates['deletedJob'] = { state: { job_id: 'deletedJob' } };
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledTimes(3);
            });
        });

        it('Should send a job-info message to the bus', () => {
            const jobId = 'foo',
                jobStateMsg = {
                    job_id: jobId,
                    state: {
                        job_id: jobId,
                    },
                },
                busMsg = {
                    jobId: jobId,
                    jobInfo: jobStateMsg,
                },
                msg = makeCommMsg('job_info', jobStateMsg);

            const comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { jobId: jobId },
                    key: { type: 'job-info' },
                });
            });
        });

        it('Should send a run_status message to the bus', () => {
            const jobId = 'foo',
                cellId = 'bar',
                busMsg = {
                    cell_id: cellId,
                    job_id: jobId,
                },
                msg = makeCommMsg('run_status', busMsg);
            const comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { cell: cellId },
                    key: { type: 'run-status' },
                });
            });
        });

        it('Should send job_canceled message to the bus', () => {
            const jobId = 'foo-canceled',
                msg = makeCommMsg('job_canceled', {
                    job_id: jobId,
                });
            const comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        via: 'job_canceled',
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-canceled' },
                    }
                );
            });
        });

        it('Should send job_does_not_exist messages to the bus', () => {
            const jobId = 'foo-dne',
                msg = makeCommMsg('job_does_not_exist', {
                    job_id: jobId,
                    source: 'someSource',
                }),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        source: 'someSource',
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-does-not-exist' },
                    }
                );
            });
        });

        it('Should send job_logs to the bus', () => {
            const jobId = 'foo-logs',
                msg = makeCommMsg('job_logs', {
                    job_id: jobId,
                    latest: true,
                    logs: [{}],
                }),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        logs: msg.content.data.content,
                        latest: msg.content.data.content.latest,
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-logs' },
                    }
                );
            });
        });

        const errCases = {
            cancel_job: 'job-cancel-error',
            job_logs: 'job-log-deleted',
            job_logs_latest: 'job-log-deleted',
            job_status: 'job-status-error',
        };

        Object.keys(errCases).forEach((errCase) => {
            it('Should handle job_comm_error of type ' + errCase, () => {
                const jobId = 'job-' + errCase,
                    errMsg = errCase + ' error happened!',
                    msg = makeCommMsg('job_comm_error', {
                        source: errCase,
                        job_id: jobId,
                        message: errMsg,
                    }),
                    comm = new JobCommChannel();
                spyOn(testBus, 'send');
                return comm.initCommChannel().then(() => {
                    comm.handleCommMessages(msg);
                    expect(testBus.send).toHaveBeenCalledWith(
                        {
                            jobId: jobId,
                            message: errMsg,
                        },
                        {
                            channel: { jobId: jobId },
                            key: { type: errCases[errCase] },
                        }
                    );
                });
            });
        });

        it('Handle unknown job errors generically', () => {
            const jobId = 'jobWithErrors',
                errMsg = 'some random error',
                requestType = 'some-error',
                busMsg = {
                    source: requestType,
                    job_id: jobId,
                    message: errMsg,
                },
                msg = makeCommMsg('job_comm_error', busMsg),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(
                    {
                        jobId: jobId,
                        message: errMsg,
                        request: requestType,
                    },
                    {
                        channel: { jobId: jobId },
                        key: { type: 'job-error' },
                    }
                );
            });
        });

        ['job_init_err', 'job_init_lookup_err'].forEach((errType) => {
            it(`Should handle ${errType}`, () => {
                const modalQuerySelector = '.modal #kb-job-err-trace';
                const msg = makeCommMsg(errType, {
                        service: 'job service',
                        error: 'An error happened!',
                        name: 'Error',
                        source: 'jobmanager',
                    }),
                    comm = new JobCommChannel();
                return comm
                    .initCommChannel()
                    .then(() => {
                        comm.handleCommMessages(msg);
                        return TestUtil.waitForElement(document.body, modalQuerySelector);
                    })
                    .then(() => {
                        expect(document.querySelector(modalQuerySelector)).not.toBeNull();
                        // click the 'OK' button
                        document
                            .querySelector('.modal a.btn.btn-default.kb-job-err-dialog__button')
                            .click();
                        // expect it to be gone
                        return TestUtil.waitForElementState(document.body, () => {
                            return document.querySelectorAll('.modal').length === 0;
                        });
                    })
                    .then(() => {
                        expect(document.querySelector(modalQuerySelector)).toBeNull();
                    });
            });
        });

        it('Should send a result message to the bus', () => {
            const cellId = 'someCellId',
                busMsg = {
                    address: { cell_id: cellId },
                    result: [1],
                },
                msg = makeCommMsg('result', busMsg),
                comm = new JobCommChannel();
            spyOn(testBus, 'send');
            return comm.initCommChannel().then(() => {
                comm.handleCommMessages(msg);
                expect(testBus.send).toHaveBeenCalledWith(busMsg, {
                    channel: { cell: cellId },
                    key: { type: 'result' },
                });
            });
        });

        it('Should handle unknown messages with console warnings', () => {
            const comm = new JobCommChannel(),
                msg = makeCommMsg('unknown_weird_msg', {});
            return comm.initCommChannel().then(() => {
                spyOn(console, 'warn');
                comm.handleCommMessages(msg);
                expect(console.warn).toHaveBeenCalled();
            });
        });
    });
});
<|endoftext|>