<commit_before>/*
This modal is very simplistic and provides to help for specific types of
content.

Controls
  - Controls should be added manually by having them call the hide method,
    or having them call loadContent with new content.

Ajax
  - In order to use this modal with ajax you need to create a request and
  on success call the loadContent method.

Images
  - In order to emulate lightbox you one will need to
*/
(function () {
    var Modal = new Class({
        Implements: [Options, Events],
        options: {
            overlayOpacity: 0.5,
            overlayZIndex: 49,
            overlayBackgroundColor: '#000000',
            overlayCssClass: null,
            panelZIndex: 50,
            panelWidth: 600,
            panelHeight: 600,
            panelBackgroundColor: '#ffffff',
            panelCssClass: null,
            // Space in the width to leave on left/right of panel.
            // - Note this isn't the sum of left and right but one
            // side only and will be mirrored.
            // - Must be integer with implicit pixel units.
            panelXOffset: 0,
            // Space in the height to leave on top/bottom of panel.
            // - Note this isn't the sum of left and right but one
            // side only and will be mirrored.
            // - Must be integer with implicit pixel units.
            panelYOffset: 0
        },
        initialize: function (options) {
            var bind = this;
            this.setOptions(options);
            this.panelEl = null;
            this.overlayEl = null;
            // True if panel and overlay are showing.
            this.showing = false;
            // True if the user customized the size.
            this.sizeCustomized = false;
            // These options are only used on one display.
            this.contentOptions = null;
            this.panelParentEvents = {
                keydown: this.panelParentKeydown.bind(this)
            };
            this.overlayEvents = {
                click: this.overlayClick.bind(this)
            };
            this.windowEvents = {
                resize: this.windowResize.bind(this),
                scroll: this.windowScroll.bind(this)
            };
        },
        attach: function () {
            /* Attach to the dom. */
            var panelParentEl = $(document.body);
            this.overlayEl = this.buildOverlay();
            this.panelEl = this.buildPanel();
            panelParentEl.adopt([this.overlayEl, this.panelEl]);
        },
        destroy: function () {
            this.hide();
            this.detachEvents();
            this.overlayEl.destroy();
            this.panelEl.destroy();
        },
        buildOverlay: function () {
            var overlayEl = new Element('div');
            overlayEl.setStyles({
                zIndex: this.options.overlayZIndex,
                margin: 0,
                padding: 0,
                position: 'absolute',
                opacity: this.options.overlayOpacity,
                backgroundColor: this.options.overlayBackgroundColor
            });
            if (this.options.overlayCssClass !== null) {
                overlayEl.addClass(this.options.overlayCssClass);
            }
            return overlayEl;
        },
        buildPanel: function () {
            var panelEl = new Element('div');
            panelEl.setStyles({
                zIndex: this.options.panelZIndex,
                margin: 0,
                padding: 0,
                position: 'absolute',
                backgroundColor: this.options.panelBackgroundColor
            });
            if (this.options.panelCssClass !== null) {
                panelEl.addClass(this.options.panelCssClass);
            }
            return panelEl;
        },
        loadContent: function (contentEl, contentOptions) {
            // TODO: We do this in hide, now quite sure we need this.
            this.panelEl.empty();
            this.panelEl.adopt(contentEl);
            this.contentOptions = contentOptions;
            if (!this.showing) {
                this.show();
            }
        },
        loadContentAutoSize: function (contentEl, contentOptions) {
            var imageUrls, onComplete, dimensions;
            imageUrls = contentEl.getElements('img').map(
                function (imageEl) {
                    return imageEl.get('src');
                });
            onComplete = (function () {
                /* Put the content in a table because it seems to be the only
                reliable way to measure the width and height with shrink
                wrapping. */
                var tdEl, trEl, tbodyEl, tableEl;
                tdEl = new Element('td').adopt(contentEl);
                trEl = new Element('tr').adopt(tdEl);
                tbodyEl = new Element('tbody').adopt(trEl);
                tableEl = new Element('table').adopt(tbodyEl);

                // Make sure the table is not shown.
                tableEl.setStyle('display', 'none');
                // Some styles might depend on being in a modal, so fake it.
                if (this.options.panelCssClass !== null) {
                    tableEl.addClass(this.options.panelCssClass);
                }
                $(document.body).adopt(tableEl);

                dimensions = tableEl.measure(function () {
                    return tableEl.getSize();
                });
                [tdEl, trEl, tbodyEl, tableEl].each(function (el) {
                    el.dispose();
                });
                contentOptions.panelWidth = dimensions.x;//width;
                contentOptions.panelHeight = dimensions.y;//height;
                this.loadContent(contentEl, contentOptions);
            }).bind(this);
            if (imageUrls.length > 0) {
                // When the images are done loading inject the clone and
                // measure it. Then render the modal with the correct
                // width/height.
                Asset.images(imageUrls, {
                    onComplete: onComplete
                });
            } else {
                onComplete();
            }
        },
        attachEvents: function () {
            var panelParentEl = $(document.body);
            panelParentEl.addEvents(this.panelParentEvents);
            this.overlayEl.addEvents(this.overlayEvents);
            window.addEvents(this.windowEvents);
        },
        detachEvents: function () {
            var panelParentEl = $(document.body);
            this.overlayEl.removeEvents(this.overlayEvents);
            panelParentEl.removeEvents(this.panelParentEvents);
            window.removeEvents(this.windowEvents);
        },
        show: function () {
            var panelParentEl;
            this.showing = true;
            this.attachEvents();
            this.overlayEl.setStyles(Object.merge(
                this.getOverlayCoords(), {
                display: 'block'
            }));
            this.panelEl.setStyles(Object.merge(
                this.getPanelCoords(), {
                display: 'block'
            }));
            this.fireEvent('panelShown');
        },
        hide: function () {
            this.showing = false;
            this.contentOptions = null;
            this.detachEvents();
            this.panelEl.setStyle('display', 'none');
            this.overlayEl.setStyle('display', 'none');
            // Clear out panel so old ids/classes aren't sitting around.
            this.panelEl.empty();
            this.fireEvent('panelHidden');
        },
        panelParentKeydown: function (e) {
            if (e.key === 'esc') {
                if (this.showing) {
                    this.hide();
                }
            }
        },
        overlayClick: function (e) {
            if (this.showing) {
                this.hide();
            }
        },
        getPanelWidth: function () {
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelWidth')) {
                return this.contentOptions.panelWidth;
            } else {
                return this.options.panelWidth;
            }
        },
        getPanelHeight: function () {
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelHeight')) {
                return this.contentOptions.panelHeight;
            } else {
                return this.options.panelHeight;
            }
        },
        getPanelXOffset: function () {
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelXOffset')) {
                return this.contentOptions.panelXOffset;
            } else {
                return this.options.panelXOffset;
            }
        },
        getPanelYOffset: function () {
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelYOffset')) {
                return this.contentOptions.panelYOffset;
            } else {
                return this.options.panelYOffset;
            }
        },
        getOverlayCoords: function () {
            var windowScrollSize, overlayCoords;
            windowScrollSize = window.getScrollSize();
            // Reposition and resize the overlay.
            overlayCoords = {
                top: 0 + 'px',
                left: 0 + 'px',
                width: windowScrollSize.x + 'px',
                height: windowScrollSize.y + 'px'
            };
            return overlayCoords;
        },
        getPanelCoords: function () {
            var windowSize, panelXMargin, panelYMargin, panelWidth,
                    panelHeight, panelCoords, panelXOffset, panelYOffset,
                    windowScroll;
            windowScroll = window.getScroll();
            windowSize = window.getSize();
            panelWidth = this.getPanelWidth();
            panelXOffset = this.getPanelXOffset();
            if (windowSize.x >= panelWidth + (panelXOffset * 2)) {
                panelXMargin = (windowSize.x - panelWidth) / 2 - panelXOffset;
            } else {
                panelXMargin = 0;
                panelWidth = windowSize.x - (panelXOffset * 2);
            }
            panelHeight = this.getPanelHeight();
            panelYOffset = this.getPanelYOffset();
            if (windowSize.y >= panelHeight + (panelYOffset * 2)) {
                panelYMargin = (windowSize.y - panelHeight) / 2 - panelYOffset;
            } else {
                panelYMargin = 0;
                panelHeight = windowSize.y - (panelYOffset * 2);
            }
            // The offsets are not included here because they lie
            // between the margin and the height/width.
            panelCoords = {
                left: panelXMargin + windowScroll.x + 'px',
                width: panelWidth + 'px',
                top: panelYMargin + windowScroll.y + 'px',
                height: panelHeight + 'px'
            };
            return panelCoords;
        },
        windowResize: function (e) {
            /* When the user resizes the window. */
            if (this.showing) {
                this.overlayEl.setStyles(this.getOverlayCoords());
                this.panelEl.setStyles(this.getPanelCoords());
            }
        },
        windowScroll: function (e) {
            /* When the user scrolls up and down in the window. */
            var targetEl;
            if (this.showing) {
                // TODO: Find a way to test if incompliant browsers
                // actually do this.  Scroll should not bubble up.
                // Only update the coords if the user
                // is not scrolling inside the modal.
                targetEl = null;
                if (typeof e !== typeof undefined) {
                    targetEl = $(e.target);
                }
                if (targetEl === null || !this.panelEl.contains(targetEl)) {
                    this.overlayEl.setStyles(this.getOverlayCoords());
                    this.panelEl.setStyles(this.getPanelCoords());
                }
            }
        }
    });

    // Until a better namespacing mechanism exists I guess this is it.
    window.Modal = Modal;

})();
<commit_msg>Fix up documentation.<commit_after>/*
Copyright (c) 2010, Ian Wilson

SEE LICENSE for the licensing information.

This modal is very simplistic and provides to help for specific types of
content.

Controls
  - Controls should be added manually by having them call the hide method,
    or having them call loadContent with new content.

Ajax
  - In order to use this modal with ajax you need to create a request and
  on success call the loadContent method.

Images
  - In order to emulate lightbox a modal instance should be created and the
  loadContent method should be called when thumbnails are clicked.

*/
(function () {
    var Modal = new Class({
        Implements: [Options, Events],
        options: {
            overlayOpacity: 0.5,
            overlayZIndex: 49,
            overlayBackgroundColor: '#000000',
            overlayCssClass: null,
            panelZIndex: 50,
            panelWidth: 600,
            panelHeight: 600,
            panelBackgroundColor: '#ffffff',
            panelCssClass: null,
            // Space in the width to leave on left/right of panel.
            // - Note this isn't the sum of left and right but one
            // side only and will be mirrored.
            // - Must be integer with implicit pixel units.
            panelXOffset: 0,
            // Space in the height to leave on top/bottom of panel.
            // - Note this isn't the sum of left and right but one
            // side only and will be mirrored.
            // - Must be integer with implicit pixel units.
            panelYOffset: 0
        },
        initialize: function (options) {
            /* Initialize the modal's properties and options.

            Must be called before attach. */
            var bind = this;
            this.setOptions(options);
            this.panelEl = null;
            this.overlayEl = null;
            // True if panel and overlay are showing.
            this.showing = false;
            // True if the user customized the size.
            this.sizeCustomized = false;
            // These options are only used on one display.
            this.contentOptions = null;
            this.panelParentEvents = {
                keydown: this.panelParentKeydown.bind(this)
            };
            this.overlayEvents = {
                click: this.overlayClick.bind(this)
            };
            this.windowEvents = {
                resize: this.windowResize.bind(this),
                scroll: this.windowScroll.bind(this)
            };
        },
        attach: function () {
            /* Attach the modal to DOM.

            Must be called before loadContent. */
            var panelParentEl = $(document.body);
            this.overlayEl = this.buildOverlay();
            this.panelEl = this.buildPanel();
            panelParentEl.adopt([this.overlayEl, this.panelEl]);
        },
        destroy: function () {
            /* Destroy this modal, it cannot be used again. */
            this.hide();
            this.detachEvents();
            this.overlayEl.destroy();
            this.panelEl.destroy();
        },
        buildOverlay: function () {
            /* Build the overlay element and set styles. */
            var overlayEl = new Element('div');
            overlayEl.setStyles({
                zIndex: this.options.overlayZIndex,
                margin: 0,
                padding: 0,
                position: 'absolute',
                opacity: this.options.overlayOpacity,
                backgroundColor: this.options.overlayBackgroundColor
            });
            if (this.options.overlayCssClass !== null) {
                overlayEl.addClass(this.options.overlayCssClass);
            }
            return overlayEl;
        },
        buildPanel: function () {
            /* Build the panel element and set styles. */
            var panelEl = new Element('div');
            panelEl.setStyles({
                zIndex: this.options.panelZIndex,
                margin: 0,
                padding: 0,
                position: 'absolute',
                backgroundColor: this.options.panelBackgroundColor
            });
            if (this.options.panelCssClass !== null) {
                panelEl.addClass(this.options.panelCssClass);
            }
            return panelEl;
        },
        loadContent: function (contentEl, contentOptions) {
            /* Load content into the modal.  The size must be provided. */
            // TODO: We do this in hide, now quite sure we need this.
            this.panelEl.empty();
            this.panelEl.adopt(contentEl);
            this.contentOptions = contentOptions;
            if (!this.showing) {
                this.show();
            }
        },
        loadContentAutoSize: function (contentEl, contentOptions) {
            /* Load content into the modal.  The size will be guessed. */
            var imageUrls, onComplete, dimensions;
            imageUrls = contentEl.getElements('img').map(
                function (imageEl) {
                    return imageEl.get('src');
                });
            onComplete = (function () {
                /* Put the content in a table because it seems to be the only
                reliable way to measure the width and height with shrink
                wrapping. */
                var tdEl, trEl, tbodyEl, tableEl;
                tdEl = new Element('td').adopt(contentEl);
                trEl = new Element('tr').adopt(tdEl);
                tbodyEl = new Element('tbody').adopt(trEl);
                tableEl = new Element('table').adopt(tbodyEl);

                // Make sure the table is not shown.
                tableEl.setStyle('display', 'none');
                // Some styles might depend on being in a modal, so fake it.
                if (this.options.panelCssClass !== null) {
                    tableEl.addClass(this.options.panelCssClass);
                }
                $(document.body).adopt(tableEl);

                dimensions = tableEl.measure(function () {
                    return tableEl.getSize();
                });
                [tdEl, trEl, tbodyEl, tableEl].each(function (el) {
                    el.dispose();
                });
                contentOptions.panelWidth = dimensions.x;//width;
                contentOptions.panelHeight = dimensions.y;//height;
                this.loadContent(contentEl, contentOptions);
            }).bind(this);
            if (imageUrls.length > 0) {
                // When the images are done loading inject the clone and
                // measure it. Then render the modal with the correct
                // width/height.
                Asset.images(imageUrls, {
                    onComplete: onComplete
                });
            } else {
                onComplete();
            }
        },
        attachEvents: function () {
            /* Attach events for modal to function. */
            var panelParentEl = $(document.body);
            panelParentEl.addEvents(this.panelParentEvents);
            this.overlayEl.addEvents(this.overlayEvents);
            window.addEvents(this.windowEvents);
        },
        detachEvents: function () {
            /* Detach events for modal to function. */
            var panelParentEl = $(document.body);
            this.overlayEl.removeEvents(this.overlayEvents);
            panelParentEl.removeEvents(this.panelParentEvents);
            window.removeEvents(this.windowEvents);
        },
        show: function () {
            /* Make the modal visible. */
            var panelParentEl;
            this.showing = true;
            this.attachEvents();
            this.overlayEl.setStyles(Object.merge(
                this.getOverlayCoords(), {
                display: 'block'
            }));
            this.panelEl.setStyles(Object.merge(
                this.getPanelCoords(), {
                display: 'block'
            }));
            this.fireEvent('panelShown');
        },
        hide: function () {
            /* Hide the modal so it is not visible. */
            this.showing = false;
            this.contentOptions = null;
            this.detachEvents();
            this.panelEl.setStyle('display', 'none');
            this.overlayEl.setStyle('display', 'none');
            // Clear out panel so old ids/classes aren't sitting around.
            this.panelEl.empty();
            this.fireEvent('panelHidden');
        },
        getPanelWidth: function () {
            /* Get the panel width from contentOptions and options. */
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelWidth')) {
                return this.contentOptions.panelWidth;
            } else {
                return this.options.panelWidth;
            }
        },
        getPanelHeight: function () {
            /* Get the panel height from contentOptions and options. */
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelHeight')) {
                return this.contentOptions.panelHeight;
            } else {
                return this.options.panelHeight;
            }
        },
        getPanelXOffset: function () {
            /* Get the panel x offset from contentOptions and options. */
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelXOffset')) {
                return this.contentOptions.panelXOffset;
            } else {
                return this.options.panelXOffset;
            }
        },
        getPanelYOffset: function () {
            /* Get the panel y offset from contentOptions and options. */
            if (this.contentOptions !== null &&
                    this.contentOptions.hasOwnProperty('panelYOffset')) {
                return this.contentOptions.panelYOffset;
            } else {
                return this.options.panelYOffset;
            }
        },
        getOverlayCoords: function () {
            /* Get the coordinates for the overlay element. */
            var windowScrollSize, overlayCoords;
            windowScrollSize = window.getScrollSize();
            // Reposition and resize the overlay.
            overlayCoords = {
                top: 0 + 'px',
                left: 0 + 'px',
                width: windowScrollSize.x + 'px',
                height: windowScrollSize.y + 'px'
            };
            return overlayCoords;
        },
        getPanelCoords: function () {
            /* Get the coordinates for the panel element. */
            var windowSize, panelXMargin, panelYMargin, panelWidth,
                    panelHeight, panelCoords, panelXOffset, panelYOffset,
                    windowScroll;
            windowScroll = window.getScroll();
            windowSize = window.getSize();
            panelWidth = this.getPanelWidth();
            panelXOffset = this.getPanelXOffset();
            if (windowSize.x >= panelWidth + (panelXOffset * 2)) {
                panelXMargin = (windowSize.x - panelWidth) / 2 - panelXOffset;
            } else {
                panelXMargin = 0;
                panelWidth = windowSize.x - (panelXOffset * 2);
            }
            panelHeight = this.getPanelHeight();
            panelYOffset = this.getPanelYOffset();
            if (windowSize.y >= panelHeight + (panelYOffset * 2)) {
                panelYMargin = (windowSize.y - panelHeight) / 2 - panelYOffset;
            } else {
                panelYMargin = 0;
                panelHeight = windowSize.y - (panelYOffset * 2);
            }
            // The offsets are not included here because they lie
            // between the margin and the height/width.
            panelCoords = {
                left: panelXMargin + windowScroll.x + 'px',
                width: panelWidth + 'px',
                top: panelYMargin + windowScroll.y + 'px',
                height: panelHeight + 'px'
            };
            return panelCoords;
        },
        panelParentKeydown: function (e) {
            /* Handles key events in the panel parent.

            Close the modal when esc is pressed. */
            if (e.key === 'esc') {
                if (this.showing) {
                    this.hide();
                }
            }
        },
        overlayClick: function (e) {
            /* Handles a click on the overlay element.

            Clicking on overlay should close the modal. */
            if (this.showing) {
                this.hide();
            }
        },
        windowResize: function (e) {
            /* Handles window resize.

            Positions and sizes the overlay and panel.
            */
            if (this.showing) {
                this.overlayEl.setStyles(this.getOverlayCoords());
                this.panelEl.setStyles(this.getPanelCoords());
            }
        },
        windowScroll: function (e) {
            /* When the user scrolls up and down in the window.

            Positions the overlay and panel.
            */
            var targetEl;
            if (this.showing) {
                // TODO: Find a way to test if incompliant browsers
                // actually do this.  Scroll should not bubble up.
                // Only update the coords if the user
                // is not scrolling inside the modal.
                targetEl = null;
                if (typeof e !== typeof undefined) {
                    targetEl = $(e.target);
                }
                if (targetEl === null || !this.panelEl.contains(targetEl)) {
                    this.overlayEl.setStyles(this.getOverlayCoords());
                    this.panelEl.setStyles(this.getPanelCoords());
                }
            }
        }
    });

    // Until a better namespacing mechanism exists I guess this is it.
    window.Modal = Modal;

})();
<|endoftext|>


<commit_before>import { loadFile, removeFile } from "./utils.es6.js";
import { Promise } from "./promise.es6.js";

export class Loader {

  /**
   * 支持加载的文件类型
   * @return {object}
   */
  static get types() {
    return {
      js: "script",
      css: "link"
    };
  }

  /**
   * 资源别名载入（依赖模式）
   * @param {object} json json格式的资源
   * @param  {...[type]} alias_names [description]
   * @return {[type]}                [description]
   */
  static alias(json, alias_names = []) {
    let batch_list = [];
    for (const name of alias_names) {
      if (!json[name]) return;
      batch_list.push(json[name]);
    }
    return this.depend.apply(this, batch_list);
  }

  /**
   * 依赖载入
   * @param  {array} batch_list [前置资源,...],[后置,...]
   * @return {promise}
   */
  static depend(...batch_list) {
    let i = 0,
      fail = [],
      done = [],
      next = (resolve, reject) => {
        if (i === batch_list.length) {
          if (fail.length > 0) {
            reject(fail);
          } else resolve(done);
        }
        this.batch.apply(this, batch_list[i])
          .then(files => {
            done = done.concat(files);
            i++;
            next(resolve, reject);
          })
          .catch(files => {
            fail = fail.concat(files);
            i++;
            next(resolve, reject);
          });
      };
    return new Promise(next);
  }

  /**
   * 并行载入
   * @param  {arguments}  files 资源,...
   * @return {promise}
   */
  static batch(...files) {
    let load_files = [],
      backup_files = [],
      fail = [],
      done = [];
    // 收集重复文件，放入备份文件
    for (const f of files) {
      let exist = false;
      for (const lf of load_files) {
        if (f.split("/").pop() === lf.split("/").pop()) {
          exist = true;
          backup_files = backup_files.concat(f);
        }
      }
      if (!exist) load_files = load_files.concat(f);
    }
    return new Promise((resolve, reject) => {
      let load = () => {
          for (const file of load_files) {
            let name = file.split("/").pop(),
              ext = name.split(".").pop(),
              attrs = { rel: file },
              type = this.types[ext];
            if (ext === "js") attrs.defer = true;
            // 之前加载过的相同文件删除
            removeFile(type, "head", file);
            loadFile(type, file, {
              attrs: attrs,
              success() {
                check(done.push(file));
              },
              error() {
                // 不留下失败文件
                removeFile(type, "head", file);
                check(fail.push(file));
              }
            });
          }
        },
        check = () => {
          if (done.length === load_files.length) {
            return resolve(done);
          }
          if (done.length + fail.length === load_files.length) {
            // 检查是否有备份，有则再尝试
            let exist = false;
            for (const fi in fail) {
              for (const bi in backup_files) {
                if (backup_files[bi].split("/").pop() ===
                  fail[fi].split("/").pop()) {
                  exist = true;
                  done = done.concat(backup_files[bi]);
                  backup_files.splice(bi, 1);
                }
              }
            }
            if (exist && done.length === load_files.length) {
              // 移除已经加载的文件
              for (const lf of load_files) {
                removeFile(this.types[lf.split(".").pop()], "head", lf);
              }
              // 替换成备份文件后能填补空缺就再执行一次
              load_files = done;
              done = [];
              fail = [];
              load();
            } else {
              reject(fail);
            }
          }
        };
      load();
    });
  }

}
<commit_msg>loader在不刷新的条件不会重复载入之前已经载入的文件。<commit_after>import { loadFile, removeFile } from "./utils.es6.js";
import { Promise } from "./promise.es6.js";

export class Loader {

  /**
   * 支持加载的文件类型
   * @return {object}
   */
  static get types() {
    return {
      js: "script",
      css: "link"
    };
  }

  /**
   * 资源别名载入（依赖模式）
   * @param {object} json json格式的资源
   * @param  {...[type]} alias_names [description]
   * @return {[type]}                [description]
   */
  static alias(json, alias_names = []) {
    let batch_list = [];
    for (const name of alias_names) {
      if (!json[name]) return;
      batch_list.push(json[name]);
    }
    return this.depend.apply(this, batch_list);
  }

  /**
   * 依赖载入
   * @param  {array} batch_list [前置资源,...],[后置,...]
   * @return {promise}
   */
  static depend(...batch_list) {
    let i = 0,
      fail = [],
      done = [],
      next = (resolve, reject) => {
        if (i === batch_list.length) {
          if (fail.length > 0) {
            reject(fail);
          } else resolve(done);
        }
        this.batch.apply(this, batch_list[i])
          .then(files => {
            done = done.concat(files);
            i++;
            next(resolve, reject);
          })
          .catch(files => {
            fail = fail.concat(files);
            i++;
            next(resolve, reject);
          });
      };
    return new Promise(next);
  }

  /**
   * 并行载入
   * @param  {arguments}  files 资源,...
   * @return {promise}
   */
  static batch(...files) {
    let load_files = [],
      backup_files = [],
      fail = [],
      done = [];
    // 已经通过loader加载过的文件
    this._loaded_files = this._loaded_files || [];
    // 收集重复文件，放入备份文件
    for (const f of files) {
      let exist = false;
      for (const lf of load_files) {
        if (f.split("/").pop() === lf.split("/").pop()) {
          exist = true;
          backup_files = backup_files.concat(f);
        }
      }
      if (!exist) load_files = load_files.concat(f);
    }
    return new Promise((resolve, reject) => {
      let load = () => {
          for (const file of load_files) {
            let name = file.split("/").pop(),
              ext = name.split(".").pop(),
              attrs = { rel: file },
              type = this.types[ext];
            if (ext === "js") attrs.defer = true;
            // 之前加载过的相同文件删除
            // removeFile(type, "head", file);
            if(this._loaded_files.indexOf(file) !== -1){
              check(done.push(file));
              continue;
            }
            loadFile(type, file, {
              attrs: attrs,
              success: () => {
                this._loaded_files.push(file);
                check(done.push(file));
              },
              error: () => {
                // 不留下失败文件
                removeFile(type, "head", file);
                check(fail.push(file));
              }
            });
          }
        },
        check = () => {
          if (done.length === load_files.length) {
            return resolve(done);
          }
          if (done.length + fail.length === load_files.length) {
            // 检查是否有备份，有则再尝试
            let exist = false;
            for (const fi in fail) {
              for (const bi in backup_files) {
                if (backup_files[bi].split("/").pop() ===
                  fail[fi].split("/").pop()) {
                  exist = true;
                  done = done.concat(backup_files[bi]);
                  backup_files.splice(bi, 1);
                }
              }
            }
            if (exist && done.length === load_files.length) {
              // 移除已经加载的文件
              for (const lf of load_files) {
                removeFile(this.types[lf.split(".").pop()], "head", lf);
              }
              // 替换成备份文件后能填补空缺就再执行一次
              load_files = done;
              done = [];
              fail = [];
              load();
            } else {
              reject(fail);
            }
          }
        };
      load();
    });
  }

}
<|endoftext|>


<commit_before>/**
 * Follow-up question blocks for jsPASAT
 */
var
  follow_up = [],
  participant_id = getParticipantId();

// post-survey notice
var task_complete_text = "
Now you will be asked a series of questions about yourself.
";
var task_complete_notice = createTextBlock(task_complete_text);
follow_up.push(task_complete_notice);

// post-survey demographics questions
var demographics_0 = {
  type: 'survey-text',
  questions: [
    ["How old are you?"],
    ["What is your date of birth? (MM/YYYY; e.g., 01/1995)"]
  ]
};

var demographics_1_questions = [
  ["Please indicate your sex:"],
  ["Which year of university are you currently in?"],
  ["What is the highest level of education that you intend to complete?"],
  ["Is English your first language?"],
  ["For how many years have you been speaking English?"],
  ["What is your mother’s highest level of education?"],
];
var demographics_1 = {
  type: 'survey-multi-choice',
  questions: demographics_1_questions,
  options: [
    [["Male", "Female"]],
    [["1st year undergrad", "2nd year undergrad", "3rd year undergrad", "4th year undergrad", "Graduated", "Post-BA/BSc continuing"]],
    [["Bachelor's degree", "Master's degree", "PhD", "Professional degree (e.g., law)"]],
    [["Yes", "No"]],
    [["Less than 1 year", "1–2 years", "3–5 years", "6–10 years", "10+ years", "All my life"]],
    [["Less than high school", "High school", "Some college", "BA/BSc degree", "MA/MSc degree", "PhD", "Professional degree (e.g., law)", "Not applicable"]],
  ],
  required: _.map(demographics_1_questions, function() {
    return [true];
  })
};
var demographics_2 = {
  type: 'survey-text',
  questions: [
    ["What is your mother's occupation?"]
  ]
};
var demographics_3_questions = [
  ["What is your father’s highest level of education?"]
];
var demographics_3 = {
  type: 'survey-multi-choice',
  questions: demographics_3_questions,
  options: [
    [["Less than high school", "High school", "Some college", "BA/BSc degree", "MA/MSc degree", "PhD", "Professional degree (e.g., law)", "Not applicable"]]
  ],
  required: _.map(demographics_3_questions, function() {
    return [true];
  })
};
var demographics_4 = {
  type: 'survey-text',
  questions: [
    ["What is your father's occupation?"],
    ["What was your final average at the end of high school?
(percentage; e.g., 75%)"],
    ["Estimate your current university average
(estimate percentage; e.g., 75%):"],
  ]
};
var demographics_5_questions = [
  ["How many statistics courses in university have you taken (or are currently taking)?"],
  ["How many statistics courses did you take in high school?"],
  ["How many years of mathematics (algebra, geometry, calculus, etc.) did you take in high school?"],
  ["How many mathematics courses in university have you taken (or are currently taking)?"],
  ["On a scale of 1–7, how much do you like math?"],
  ["Have you been previously diagnosed with ADD or AD/HD?"],
];
var demographics_5 = {
  type: 'survey-multi-choice',
  questions: demographics_5_questions,
  options: [
    [["None", "1", "2", "3", "4", "5", "More than 5"]],
    [["None", "1", "2", "3", "4", "5", "More than 5"]],
    [["None", "1", "2", "3", "4", "More than 4"]],
    [["None", "1", "2", "3", "4", "5", "More than 5"]],
    [jsPASAT.LIKERT_SCALE_1],
    [["Yes", "No"]],
  ],
  required: _.map(demographics_5_questions, function() {
    return [true];
  }),
  horizontal: true
};
var demographics_6 = {
  type: 'survey-text',
  questions: [
    ["Please indicate what your current (or intended) university major is:"]
  ]
};

// extended questions
var lead_in_question = "When using computers and/or electronic media, do you ",
    extended_questions = [
      [lead_in_question + "open attachments in emails that are sent by someone you don’t know?"],
      [lead_in_question + "backup important work and documents on your computer?"],
      [lead_in_question + "have time limits on how much recreational time you spend using those electronic media at home?"],
      [lead_in_question + "use the privacy settings on social networking sites, such as Facebook and Twitter, to protect your personal information and privacy?"],
      ["Do you have anti-virus software on your home computer(s)?"],
      ["Have you ever had a computer virus?"],
    ];
var extended_questions_1 = {
  type: 'survey-multi-choice',
  questions: extended_questions,
  options: _.map(extended_questions,
    function(){ return [["Yes", "No"]];
  }),
  required: _.map(extended_questions, function() {
    return [true];
  })
};
var extended_questions_2 = {
  type: 'survey-text',
  questions: [
    ["How many times have you had a computer virus?"]
  ]
};

// behavioural survey
var lead_in_school = "At school, have you had ",
    lead_in_life = "In general, have you ",
    behavioural_survey_questions = [
        [lead_in_school + "problems taking notes?"],
        [lead_in_school + "problems completing assignments?"],
        [lead_in_school + "problems getting your work done efficiently ?"],
        [lead_in_school + "problems with instructors?"],
        [lead_in_school + "problems meeting minimum requirements to stay in school?"],
        [lead_in_school + "problems with attendance?"],
        [lead_in_school + "problems with being late?"],
        [lead_in_school + "problems with working to your potential?"],
        [lead_in_school + "problems with inconsistent grades?"],
        [lead_in_life + "made excessive or inappropriate use of internet, video games or TV?"],
        [lead_in_life + "had problems getting ready to leave the house?"],
        [lead_in_life + "had problems getting to bed?"],
        [lead_in_life + "had problems with eating junk food?"],
        [lead_in_life + "gotten hurt or injured?"],
        [lead_in_life + "been avoiding exercise?"],
        [lead_in_life + "had problems attending regular appointments, such as doctor/dentist?"],
        [lead_in_life + "had problems managing chores at home?"],
        [lead_in_life + "had problems managing money?"]
    ],
    behavioural_survey_likert = [
      "0
Never or
not at all",
      "1
Sometimes
or somewhat",
      "2
Often or
very much",
      "3
Very often
or very much",
      "N/A"
    ],
    behavioural_survey_preamble = "For these next items, please indicate how the emotional or behavioural problems listed might have affected you in the last month.";
var behavioural_survey_notice = createTextBlock(behavioural_survey_preamble);
var behavioural_survey = {
  type: 'survey-multi-choice',
  questions: behavioural_survey_questions,
  options: _.map(behavioural_survey_questions,
    function(){ return [behavioural_survey_likert];
  }),
  required: _.map(behavioural_survey_questions, function() {
    return [true];
  }),
  horizontal: true,
  randomize_order: true
};

follow_up.push(
  demographics_0,
  demographics_1,
  demographics_2,
  demographics_3,
  demographics_4,
  demographics_5,
  demographics_6,
  extended_questions_1,
  extended_questions_2,
  behavioural_survey_notice,
  behavioural_survey
);

// retrospective questions notice
var retrospective_survey_text = "
Now we are going to ask you some questions about the working memory task you completed previously – that is, the task where numbers were presented to you on a computer screen one at a time and you had to add them up.
";
var retrospective_survey_notice = createTextBlock(retrospective_survey_text);

// retrospective questions
var retrospective_survey_questions = [
  ["On this working memory task, what was your total amount of mental effort?"],
  ["On this working memory task, what was your total amount of discomfort or distress?"],
  ["How much did you enjoy doing this working memory task?"],
  ["How well did you perform on the working memory task?"],
  ["How much mental fatigue did you have during the working memory task?"],
  ["How satisfied are you with your performance on the working memory task?"],
  ["How willing would you be to do another working memory task right now?"],
];
var retrospective_survey = {
  type: 'survey-multi-choice',
  questions: retrospective_survey_questions,
  options: [
    [jsPASAT.LIKERT_SCALE_1],
    [jsPASAT.LIKERT_SCALE_1],
    [jsPASAT.LIKERT_SCALE_1],
    [jsPASAT.LIKERT_SCALE_2],
    [jsPASAT.LIKERT_SCALE_1],
    [["1
Not at all
satisfied", "2", "3", "4", "5", "6", "7
Completely
satisfied"]],
    [["1
Not at all
willing", "2", "3", "4", "5", "6", "7
Definitely
willing"]],
  ],
  required: _.map(retrospective_survey_questions, function() {
    return [true];
  }),
  horizontal: true
};

// don't include retrospective questions if the experiment was skipped
var url_params = getUrlParams();
if (!_.has(url_params, 'skip_experiment')) {
  follow_up.push(retrospective_survey_notice);
  follow_up.push(retrospective_survey);
}

// add generated experiment settings to saved data
jsPsych.data.addProperties({
  participant_id: participant_id,
});

jsPsych.init({
  experiment_structure: follow_up,
  display_element: $('#jspsych-target'),
  on_finish: function() {
    postDataToDb(jsPsych.data.getData(), participant_id, 'finish');
  }
});
<commit_msg>Update follow-up questions formatting (part 1)<commit_after>/**
 * Follow-up question blocks for jsPASAT
 */
var
  follow_up = [],
  participant_id = getParticipantId();

// post-survey notice
var task_complete_text = "
Now you will be asked a series of questions about yourself.
";
var task_complete_notice = createTextBlock(task_complete_text);
follow_up.push(task_complete_notice);

// post-survey demographics questions
// ---------------------------------

// age questions
var demographics_age = {
  type: 'survey-text',
  timeline: [
    {questions: ["How old are you?"]},
    {questions: [
      "What is your date of birth? (MM/YYYY; e.g., 01/1995)"
    ]}
  ]
};

// part 1
var demographics_1_questions = [
  {
    questions: ["Please indicate your sex:"],
    options: [["Male", "Female"]]
  },
  {
    questions: ["Which year of university are you currently in?"],
    options: [[
      "1st year undergrad", "2nd year undergrad", "3rd year undergrad",
      "4th year undergrad", "Graduated", "Post-BA/BSc continuing"
    ]]
  },
  {
    questions: ["What is the highest level of education that you intend to complete?"],
    options: [[
      "Bachelor's degree", "Master's degree", "PhD",
      "Professional degree (e.g., law)"
    ]]
  },
  {
    questions: ["Is English your first language?"],
    options: [["Yes", "No"]]
  },
  {
    questions: ["For how many years have you been speaking English?"],
    options: [[
      "Less than 1 year", "1–2 years", "3–5 years", "6–10 years",
      "10+ years", "All my life"
    ]]
  },
  {
    questions: ["What is your mother’s highest level of education?"],
    options: [[
      "Less than high school", "High school", "Some college", "BA/BSc degree",
      "MA/MSc degree", "PhD", "Professional degree (e.g., law)",
      "Not applicable"
    ]]
  }
];
var demographics_1 = {
  type: 'survey-multi-choice',
  timeline: demographics_1_questions,
  required: _.map(demographics_1_questions, function() { return true; })
};

// part 2
var demographics_2 = {
  type: 'survey-text',
  questions: [
    ["What is your mother's occupation?"]
  ]
};

// part 3
var demographics_3_questions = [
  {
    questions: ["What is your father’s highest level of education?"],
    options: [
      ["Less than high school", "High school", "Some college", "BA/BSc degree", "MA/MSc degree", "PhD", "Professional degree (e.g., law)", "Not applicable"]
    ]
  },
];
var demographics_3 = {
  type: 'survey-multi-choice',
  timeline: demographics_3_questions,
  required: _.map(demographics_3_questions, function() { return true; })
};

// part 4
var demographics_4_questions = [
  ["What is your father's occupation?"],
  ["What was your final average at the end of high school?
(percentage; e.g., 75%)"],
  ["Estimate your current university average
(estimate percentage; e.g., 75%):"],
];
var demographics_4 = {
  type: 'survey-text',
  timeline: _.map(demographics_4_questions, function(q) {
    return {questions: q};
  })
};

// part 5
var demographics_5_questions = [
  {
    questions: ["How many statistics courses in university have you taken (or are currently taking)?"],
    options: ["None", "1", "2", "3", "4", "5", "More than 5"],
  },
  {
    questions: ["How many statistics courses did you take in high school?"],
    options: ["None", "1", "2", "3", "4", "5", "More than 5"],
  },
  {
    questions: ["How many years of mathematics (algebra, geometry, calculus, etc.) did you take in high school?"],
    options: ["None", "1", "2", "3", "4", "More than 4"],
  },
  {
    questions: ["How many mathematics courses in university have you taken (or are currently taking)?"],
    options: ["None", "1", "2", "3", "4", "5", "More than 5"],
  },
  {
    questions: ["On a scale of 1–7, how much do you like math?"],
    options: jsPASAT.LIKERT_SCALE_1,
  },
  {
    questions: ["Have you been previously diagnosed with ADD or AD/HD?"],
    options: ["Yes", "No"],
  },
];
var demographics_5 = {
  type: 'survey-multi-choice',
  timeline: demographics_5_questions,
  required: _.map(demographics_5_questions, function() { return true; }),
  horizontal: true
};

// part 6
var demographics_6 = {
  type: 'survey-text',
  questions: [
    ["Please indicate what your current (or intended) university major is:"]
  ]
};

// extended questions
var lead_in_question = "When using computers and/or electronic media, do you ";
var extended_questions = [
      lead_in_question + "open attachments in emails that are sent by someone you don’t know?",
      lead_in_question + "backup important work and documents on your computer?",
      lead_in_question + "have time limits on how much recreational time you spend using those electronic media at home?",
      lead_in_question + "use the privacy settings on social networking sites, such as Facebook and Twitter, to protect your personal information and privacy?",
      "Do you have anti-virus software on your home computer(s)?",
      "Have you ever had a computer virus?",
    ];
var extended_questions_1 = {
  type: 'survey-multi-choice',
  questions: extended_questions,
  options: _.map(extended_questions,
    function(){ return ["Yes", "No"];
  }),
  required: _.map(extended_questions, function() {
    return true;
  })
};
var extended_questions_2 = {
  type: 'survey-text',
  questions: [
    ["How many times have you had a computer virus?"]
  ]
};

// behavioural survey
var lead_in_school = "At school, have you had ",
    lead_in_life = "In general, have you ",
    behavioural_survey_questions = [
      lead_in_school + "problems taking notes?",
      lead_in_school + "problems completing assignments?",
      lead_in_school + "problems getting your work done efficiently ?",
      lead_in_school + "problems with instructors?",
      lead_in_school + "problems meeting minimum requirements to stay in school?",
      lead_in_school + "problems with attendance?",
      lead_in_school + "problems with being late?",
      lead_in_school + "problems with working to your potential?",
      lead_in_school + "problems with inconsistent grades?",
      lead_in_life + "made excessive or inappropriate use of internet, video games or TV?",
      lead_in_life + "had problems getting ready to leave the house?",
      lead_in_life + "had problems getting to bed?",
      lead_in_life + "had problems with eating junk food?",
      lead_in_life + "gotten hurt or injured?",
      lead_in_life + "been avoiding exercise?",
      lead_in_life + "had problems attending regular appointments, such as doctor/dentist?",
      lead_in_life + "had problems managing chores at home?",
      lead_in_life + "had problems managing money?"
    ],
    behavioural_survey_likert = [
      "0
Never or
not at all",
      "1
Sometimes
or somewhat",
      "2
Often or
very much",
      "3
Very often
or very much",
      "N/A"
    ],
    behavioural_survey_preamble = "For these next items, please indicate how the emotional or behavioural problems listed might have affected you in the last month.";
var behavioural_survey_notice = createTextBlock(behavioural_survey_preamble);
var behavioural_survey = {
  type: 'survey-multi-choice',
  questions: behavioural_survey_questions,
  options: _.map(behavioural_survey_questions,
    function(){ return behavioural_survey_likert; }),
  required: _.map(behavioural_survey_questions, function() {
    return [true]; }),
  horizontal: true,
  randomize_order: true
};

follow_up.push(
  demographics_age,
  demographics_1,
  demographics_2,
  demographics_3,
  demographics_4,
  demographics_5,
  demographics_6,
  extended_questions_1,
  extended_questions_2,
  behavioural_survey_notice,
  behavioural_survey
);

// retrospective questions notice
var retrospective_survey_text = "
Now we are going to ask you some questions about the working memory task you completed previously – that is, the task where numbers were presented to you on a computer screen one at a time and you had to add them up.
";
var retrospective_survey_notice = createTextBlock(retrospective_survey_text);

// retrospective questions
var retrospective_survey_questions = [
  "On this working memory task, what was your total amount of mental effort?",
  "On this working memory task, what was your total amount of discomfort or distress?",
  "How much did you enjoy doing this working memory task?",
  "How well did you perform on the working memory task?",
  "How much mental fatigue did you have during the working memory task?",
  "How satisfied are you with your performance on the working memory task?",
  "How willing would you be to do another working memory task right now?",
];
var retrospective_survey = {
  type: 'survey-multi-choice',
  questions: retrospective_survey_questions,
  options: [
    jsPASAT.LIKERT_SCALE_1,
    jsPASAT.LIKERT_SCALE_1,
    jsPASAT.LIKERT_SCALE_1,
    jsPASAT.LIKERT_SCALE_2,
    jsPASAT.LIKERT_SCALE_1,
    ["1
Not at all
satisfied", "2", "3", "4", "5", "6", "7
Completely
satisfied"],
    ["1
Not at all
willing", "2", "3", "4", "5", "6", "7
Definitely
willing"],
  ],
  required: _.map(retrospective_survey_questions, function() {
    return [true];
  }),
  horizontal: true
};

// don't include retrospective questions if the experiment was skipped
var url_params = getUrlParams();
if (!_.has(url_params, 'skip_experiment')) {
  follow_up.push(retrospective_survey_notice);
  follow_up.push(retrospective_survey);
}

// add generated experiment settings to saved data
jsPsych.data.addProperties({
  participant_id: participant_id,
});

jsPsych.init({
  display_element: $('#jspsych-target'),
  timeline: follow_up,
  on_finish: function() {
    postDataToDb(jsPsych.data.getData(), participant_id, 'finish');
  }
});
<|endoftext|>


<commit_before>var tml = {
    fillJobCommonFields: function (html, job){

        html.find(".id").addClass("row-" + job._status);

        html.find("[data-id]").text(job._id);
        html.find("[data-target-module]").text(job._targetModule);
        html.find("[data-target-version]").text(job._targetVersion);
        html.find("[data-status]").text(job._status);
        html.find(".label").addClass("label-" + job._status);
        html.find("[data-queued-date]").text(job._queued_ago);
        html.find("[data-updated-date]").text(job._updated_ago);
        // ---- User
        tml.displayTooltip(job._user, html.find("[data-user]"));
        tml.displayTooltip(job._queued_at, html.find("[data-queue]"));
        tml.displayTooltip(job._updated_at, html.find("[data-update]"));

        // ---- Ticket
        tml.displayTicket(job._ticket, html.find("[data-ticket]"));
        // ---- Common actions
        tml.displayActions(job._buildJobUrl, html.find("[data-jenkins]"));
        tml.displayActions(job._deployJobUrl, html.find("[data-deployment]"));
        tml.displayActions(job._testJobUrl, html.find("[data-test-job]"));
        // ---- Start tooltip
        html.find("[data-toggle='tooltip']").tooltip();
    },

    fillRepoFields: function (html, repo){
        html.find("[data-repo]").text(repo._module).attr("href", repo._module_link);
        html.find("[data-testing]").text(repo._testing).attr("href", repo._testing_link);
        html.find("[data-testing-time]").text(repo._testing_time);
        html.find("[data-staging]").text(repo._staging).attr("href", repo._staging_link);
        html.find("[data-staging-time]").text(repo._staging_time);
        html.find("[data-production]").text(repo._production).attr("href", repo._production_link);
        html.find("[data-production-time]").text(repo._production_time);
        // ---- Ticket
        tml.displayTicket(repo._ticket, html.find("[data-ticket]"));
        // ---- Start tooltip
        html.find("[data-toggle='tooltip']").tooltip();
    },

    handleDate: function (jobDate, currentDate){
        var current = currentDate,
            job = new Date(jobDate),
            days,
            hours,
            minutes;

        minutes = (Math.abs(current - job) / 1000) / 60;
        hours = minutes / 60;
        days = hours / 24;

        minutes = parseInt(minutes);
        hours = parseInt(hours);
        days = parseInt(days);

        if (minutes < 60) {
            return minutes + " min ago";
        } else if (hours < 24) {
            return hours + " hours ago";
        } else if (!isNaN(days)) {
            return days + " days ago";
        } else {
            return "-";
        }
    },

    displayTooltip: function (data, selector){
        if (data != "") {
            selector.attr("title", data);
            selector.show();
        }
    },

    displayTicket: function (data, selector){
        if (data != "") {
            selector.attr("title", data);
            selector.attr("href", data);
            selector.show();
        }

    },

    displayActions: function (data, selector, toJson) {
        var toJson = toJson || false;
        if(toJson && data != "") {
            selector.data("job-id", data.id);
            selector.data("job-targetModule", data.targetModule);
            selector.data("job-targetVersion", data.targetVersion);
            selector.show();
        } else {
            if(data != "") {
                selector.attr("href", data);
                selector.show();
            }
        }
    },

    /* -------------------------
       -- Fill job template ---
       ------------------------- */
    preprodQueue: function (job) {
        var newJob = $("#resources .job").clone(),
            remove = "",
            date = new Date(job._serverTime);

        if(job._canCancel) {
            remove = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(remove, newJob.find("[data-remove]"), true);

        return newJob;
    },

    preprodInProgress: function (job) {
        var newJob = $("#resources .job").clone(),
            date = new Date(job._serverTime);

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);

        return newJob;
    },

    preprodDeployed: function (job) {
        var newJob = $("#resources .job").clone(),
            canGoLive = job._canGoLive,
            goLive = "",
            date = new Date(job._serverTime);

        if (canGoLive){
            goLive = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }
        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(goLive, newJob.find("[data-go-live]"), true);

        return newJob;
    },

    prodQueue: function (job) {
        var newJob = $("#resources .job").clone(),
            remove = "",
            date = new Date(job._serverTime);

        if(job._canCancel) {
            remove = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(remove, newJob.find("[data-remove]"), true);

        return newJob;
    },

    prodInProgress: function (job) {
        var newJob = $("#resources .job").clone(),
            viewLiveJob = job._deployLiveJobUrl,
            date = new Date(job._serverTime);

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(viewLiveJob, newJob.find("[data-live-job]"));

        return newJob;
    },

    prodDeployed: function (job) {
        var newJob = $("#resources .job").clone(),
            viewLiveJob = job._deployLiveJobUrl,
            wentLive = job._canRollback,
            redeploy = "",
            date = new Date(job._serverTime);

        if (wentLive){
            redeploy = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(viewLiveJob, newJob.find("[data-live-job]"));
        tml.displayActions(redeploy, newJob.find("[data-redeploy]"), true);

        return newJob;
    },

    /* -------------------------
       -- Fill repo template ---
       ------------------------- */
    version: function (version) {
        var newRepo = $("#resources .repo").clone(),
            canRollback = false, //repo._canRollback
            goLive = "",
            rollback = "";

        if (canRollback){
            rollback = {
                "id": version._id,
                "targetModule": version._module,
            }
        }
        tml.fillRepoFields(newRepo, version);

        if (typeof version._prod_ready != 'undefined')
          if (version._prod_ready != version._production)
            if (version._canGoLive) {
              goLive = {
                  "targetModule": version._module,
                  "targetVersion": version._prod_ready
              };
            }
            prod_label = newRepo.find("[data-production]");
            prod_label.attr("href", version._prod_ready_link);
            prod_label.css("color", "red");
            tml.displayTooltip("Production ready", prod_label);
        }
        tml.displayActions(goLive, newRepo.find("[data-conf-live]"), true);

        //tml.displayActions(rollback, newRepo.find("[data-rollback]"), true);

        return newRepo;
    }
}
<commit_msg>Fixes 3<commit_after>var tml = {
    fillJobCommonFields: function (html, job){

        html.find(".id").addClass("row-" + job._status);

        html.find("[data-id]").text(job._id);
        html.find("[data-target-module]").text(job._targetModule);
        html.find("[data-target-version]").text(job._targetVersion);
        html.find("[data-status]").text(job._status);
        html.find(".label").addClass("label-" + job._status);
        html.find("[data-queued-date]").text(job._queued_ago);
        html.find("[data-updated-date]").text(job._updated_ago);
        // ---- User
        tml.displayTooltip(job._user, html.find("[data-user]"));
        tml.displayTooltip(job._queued_at, html.find("[data-queue]"));
        tml.displayTooltip(job._updated_at, html.find("[data-update]"));

        // ---- Ticket
        tml.displayTicket(job._ticket, html.find("[data-ticket]"));
        // ---- Common actions
        tml.displayActions(job._buildJobUrl, html.find("[data-jenkins]"));
        tml.displayActions(job._deployJobUrl, html.find("[data-deployment]"));
        tml.displayActions(job._testJobUrl, html.find("[data-test-job]"));
        // ---- Start tooltip
        html.find("[data-toggle='tooltip']").tooltip();
    },

    fillRepoFields: function (html, repo){
        html.find("[data-repo]").text(repo._module).attr("href", repo._module_link);
        html.find("[data-testing]").text(repo._testing).attr("href", repo._testing_link);
        html.find("[data-testing-time]").text(repo._testing_time);
        html.find("[data-staging]").text(repo._staging).attr("href", repo._staging_link);
        html.find("[data-staging-time]").text(repo._staging_time);
        html.find("[data-production]").text(repo._production).attr("href", repo._production_link);
        html.find("[data-production-time]").text(repo._production_time);
        // ---- Ticket
        tml.displayTicket(repo._ticket, html.find("[data-ticket]"));
        // ---- Start tooltip
        html.find("[data-toggle='tooltip']").tooltip();
    },

    handleDate: function (jobDate, currentDate){
        var current = currentDate,
            job = new Date(jobDate),
            days,
            hours,
            minutes;

        minutes = (Math.abs(current - job) / 1000) / 60;
        hours = minutes / 60;
        days = hours / 24;

        minutes = parseInt(minutes);
        hours = parseInt(hours);
        days = parseInt(days);

        if (minutes < 60) {
            return minutes + " min ago";
        } else if (hours < 24) {
            return hours + " hours ago";
        } else if (!isNaN(days)) {
            return days + " days ago";
        } else {
            return "-";
        }
    },

    displayTooltip: function (data, selector){
        if (data != "") {
            selector.attr("title", data);
            selector.show();
        }
    },

    displayTicket: function (data, selector){
        if (data != "") {
            selector.attr("title", data);
            selector.attr("href", data);
            selector.show();
        }

    },

    displayActions: function (data, selector, toJson) {
        var toJson = toJson || false;
        if(toJson && data != "") {
            selector.data("job-id", data.id);
            selector.data("job-targetModule", data.targetModule);
            selector.data("job-targetVersion", data.targetVersion);
            selector.show();
        } else {
            if(data != "") {
                selector.attr("href", data);
                selector.show();
            }
        }
    },

    /* -------------------------
       -- Fill job template ---
       ------------------------- */
    preprodQueue: function (job) {
        var newJob = $("#resources .job").clone(),
            remove = "",
            date = new Date(job._serverTime);

        if(job._canCancel) {
            remove = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(remove, newJob.find("[data-remove]"), true);

        return newJob;
    },

    preprodInProgress: function (job) {
        var newJob = $("#resources .job").clone(),
            date = new Date(job._serverTime);

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);

        return newJob;
    },

    preprodDeployed: function (job) {
        var newJob = $("#resources .job").clone(),
            canGoLive = job._canGoLive,
            goLive = "",
            date = new Date(job._serverTime);

        if (canGoLive){
            goLive = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }
        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(goLive, newJob.find("[data-go-live]"), true);

        return newJob;
    },

    prodQueue: function (job) {
        var newJob = $("#resources .job").clone(),
            remove = "",
            date = new Date(job._serverTime);

        if(job._canCancel) {
            remove = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(remove, newJob.find("[data-remove]"), true);

        return newJob;
    },

    prodInProgress: function (job) {
        var newJob = $("#resources .job").clone(),
            viewLiveJob = job._deployLiveJobUrl,
            date = new Date(job._serverTime);

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(viewLiveJob, newJob.find("[data-live-job]"));

        return newJob;
    },

    prodDeployed: function (job) {
        var newJob = $("#resources .job").clone(),
            viewLiveJob = job._deployLiveJobUrl,
            wentLive = job._canRollback,
            redeploy = "",
            date = new Date(job._serverTime);

        if (wentLive){
            redeploy = {
                "id": job._id,
                "targetModule": job._targetModule,
                "targetVersion": job._targetVersion
            };
        }

        // convert date to time
        job._queued_ago = tml.handleDate(job._queued_at, date);
        job._updated_ago = tml.handleDate(job._updated_at, date);

        tml.fillJobCommonFields(newJob, job);
        tml.displayActions(viewLiveJob, newJob.find("[data-live-job]"));
        tml.displayActions(redeploy, newJob.find("[data-redeploy]"), true);

        return newJob;
    },

    /* -------------------------
       -- Fill repo template ---
       ------------------------- */
    version: function (version) {
        var newRepo = $("#resources .repo").clone(),
            canRollback = false, //repo._canRollback
            goLive = "",
            rollback = "";

        if (canRollback){
            rollback = {
                "id": version._id,
                "targetModule": version._module,
            }
        }
        tml.fillRepoFields(newRepo, version);

        if (typeof version._prod_ready != 'undefined'){
          if (version._prod_ready != version._production){
            if (version._canGoLive) {
              goLive = {
                  "targetModule": version._module,
                  "targetVersion": version._prod_ready
              };
            }
            prod_label = newRepo.find("[data-production]");
            prod_label.attr("href", version._prod_ready_link);
            prod_label.css("color", "red");
            tml.displayTooltip("Production ready", prod_label);
          }
        }
        tml.displayActions(goLive, newRepo.find("[data-conf-live]"), true);

        //tml.displayActions(rollback, newRepo.find("[data-rollback]"), true);

        return newRepo;
    }
}
<|endoftext|>


<commit_before>'use strict'

class Brick extends Rectangle {
    constructor(x, y, width, height, strength=2) {
        super(x, y, width, height)

        this.maxStrength = strength
        this.strength = strength

        this.top = new Segment(x, y, x + width, y)
        this.right = new Segment(x + width, y, x + width, y + height)
        this.bottom = new Segment(x, y + height, x + width, y + height)
        this.left = new Segment(x, y, x, y + height)

        this.top.show = false
        this.right.show = false
        this.bottom.show = false
        this.left.show = false
    }

    draw(g, showEdges=false) {
        if (this.strength > 0) {
            let fillAlpha = this.strength / this.maxStrength
            super.draw(g, 1, 'rgba(255, 0, 255, 1.0)', `rgba(255, 0, 255, ${fillAlpha})`)
            if(showEdges) {
                if(this.top.show) {
                    this.top.draw(g, 2, '#00f')
                    // this.top.show = false
                }
                if(this.right.show) {
                    this.right.draw(g, 2, '#00f')
                    // this.right.show = false
                }
                if(this.bottom.show) {
                    this.bottom.draw(g, 2, '#00f')
                    // this.bottom.show = false
                }
                if(this.left.show) {
                    this.left.draw(g, 2, '#00f')
                    // this.left.show = false
                }
            }
        }
    }

    detectCollision(ball) {
        if (this.strength > 0) {
            if(circleTouchesSegment(ball, this.bottom)) {
                this.strength--
                this.bottom.show = true
                ball.switchY()
                return true
            }
            else if(circleTouchesSegment(ball, this.right)) {
                this.strength--
                this.right.show = true
                ball.switchX()
                return true
            }
            else if(circleTouchesSegment(ball, this.top)) {
                this.strength--
                this.top.show = true
                ball.switchY()
                return true
            }
            else if(circleTouchesSegment(ball, this.left)) {
                this.strength--
                this.left.show = true
                ball.switchX()
                return true
            }
        }
        return false
    }
}

class Bricks {
    constructor(rows=5, cols=9, width=60, height=30, padding=5, offsetTop=120, offsetLeft=30) {
        let strength = 2
        this.rows = rows
        this.cols = cols
        this.remaining = this.rows * this.cols
        this.bricks = []
        for (let i=0; i < this.cols; i++) {
            this.bricks[i] = []
            for (let j=0; j < this.rows; j++) {
                let x = (i * (width + padding)) + offsetLeft
                let y = (j * (height + padding)) + offsetTop
                this.bricks[i][j] = new Brick(x, y, width, height, strength)
            }
        }
    }

    draw(g, showEdges=true) {
        for(let i=0; i < this.cols; i++) {
            for(let j=0; j < this.rows; j++) {
                this.bricks[i][j].draw(g, showEdges)
            }
        }
    }

    detectCollision(ball) {
        for(let i=0; i < this.cols; i++) {
            for(let j=0; j < this.rows; j++) {
                let brick = this.bricks[i][j]
                if(brick.detectCollision(ball)) {
                    if(brick.strength < 1) {
                        this.remaining--
                    }
                    return true
                }
            }
        }
        return false
    }
}
<commit_msg>- varying the brick colors<commit_after>'use strict'

class Brick extends Rectangle {
    constructor(x, y, width, height, color, strength=1) {
        super(x, y, width, height)
        this.color = color

        this.maxStrength = strength
        this.strength = strength

        this.top = new Segment(x, y, x + width, y)
        this.right = new Segment(x + width, y, x + width, y + height)
        this.bottom = new Segment(x, y + height, x + width, y + height)
        this.left = new Segment(x, y, x, y + height)

        this.top.show = false
        this.right.show = false
        this.bottom.show = false
        this.left.show = false
    }

    draw(g, showEdges=false) {
        if (this.strength > 0) {
            let fillAlpha = this.strength / this.maxStrength
            super.draw(g, 1, this.color, this.color)
            if(showEdges) {
                if(this.top.show) {
                    this.top.draw(g, 2, '#00f')
                    // this.top.show = false
                }
                if(this.right.show) {
                    this.right.draw(g, 2, '#00f')
                    // this.right.show = false
                }
                if(this.bottom.show) {
                    this.bottom.draw(g, 2, '#00f')
                    // this.bottom.show = false
                }
                if(this.left.show) {
                    this.left.draw(g, 2, '#00f')
                    // this.left.show = false
                }
            }
        }
    }

    detectCollision(ball) {
        if (this.strength > 0) {
            if(circleTouchesSegment(ball, this.bottom)) {
                this.strength--
                this.bottom.show = true
                ball.switchY()
                return true
            }
            else if(circleTouchesSegment(ball, this.right)) {
                this.strength--
                this.right.show = true
                ball.switchX()
                return true
            }
            else if(circleTouchesSegment(ball, this.top)) {
                this.strength--
                this.top.show = true
                ball.switchY()
                return true
            }
            else if(circleTouchesSegment(ball, this.left)) {
                this.strength--
                this.left.show = true
                ball.switchX()
                return true
            }
        }
        return false
    }
}

class Bricks {
    constructor(rows=6, cols=9, width=60, height=30, padding=5, offsetTop=120, offsetLeft=30) {
        this.brickTypes = [
            {color: '#808080', strength: 5},
            {color: '#ff0000', strength: 1},
            {color: '#ffff00', strength: 1},
            {color: '#0000ff', strength: 1},
            {color: '#ff00ff', strength: 1},
            {color: '#00ff00', strength: 1},
        ]

        this.rows = rows
        this.cols = cols
        this.remaining = this.rows * this.cols
        this.bricks = []

        for (let i=0; i < this.cols; i++) {
            this.bricks[i] = []
            for (let j=0; j < this.rows; j++) {
                let brickType = this.brickTypes[j]
                let x = (i * (width + padding)) + offsetLeft
                let y = (j * (height + padding)) + offsetTop
                this.bricks[i][j] = new Brick(x, y, width, height, brickType.color, brickType.strength)
            }
        }
    }

    draw(g, showEdges=true) {
        for(let i=0; i < this.cols; i++) {
            for(let j=0; j < this.rows; j++) {
                this.bricks[i][j].draw(g, showEdges)
            }
        }
    }

    detectCollision(ball) {
        for(let i=0; i < this.cols; i++) {
            for(let j=0; j < this.rows; j++) {
                let brick = this.bricks[i][j]
                if(brick.detectCollision(ball)) {
                    if(brick.strength < 1) {
                        this.remaining--
                    }
                    return true
                }
            }
        }
        return false
    }
}
<|endoftext|>