<commit_before>/*
 * chronos.js
 *
 * Created by happyCoda on 20.10.2015.
 * Copyright (c) 2015 happyCoda. All rights reserved.
 */

var ChronosWrapper = function ChronosWrapper() {

    'use strict';

    /**
     * A module for creating timers.
     * @exports Chronos
     */
    var Chronos = {};

    /**
     * @method get
     * Factory method that returns any other property of Chronos
     *
     * @param {String} prop any string
     * @returns {*} this[prop] any property value or undefined
     */
    Chronos.get = function get(prop) {
        // TODO: make this method to return only string, array or object represents calculated time
        return this[prop];
    };

    /**
     * @method getCurrentTime
     * Gets current time
     *
     * @returns {Object} currentTime current time represented as a Date object
     */
    Chronos.getCurrentTime = function getCurrentTime() {

        var currentTime = new Date();

        return currentTime;

    };

    /**
     * @method getFutureTime
     * Gets future time for backwards count
     *
     * @returns {Object} this.futureTime future time represented as a Date object
     */
    Chronos.getFutureTime = function getFutureTime() {

        this.futureTime = new Date(this.timeString);

        return this.futureTime;
    };

    /**
     * @method getPastTime
     * Gets past time for forward count
     *
     * @returns {Object} this.pastTime past time represented as a Date object
     */
    Chronos.getPastTime = function getPastTime() {
        var currentDateString;

        currentDateString = this.getCurrentTime().toDateString();

        this.pastTime = new Date(currentDateString + ' 00:00');

        return this.pastTime;
    };

    /**
     * @method getDiff
     * Calculates a difference between current and target time
     *
     * @param {Number} time1 timestamp from current or future time
     * @param {Number} time2 timestamp from past or current time
     * @returns {Number} diff difference between current and target timestamps
     */
    Chronos.getDiff = function getDiff(time1, time2) {

        var diff;

        diff = time1 - time2;

        return diff;

    };

    /**
     * @method calculateUnits
     * Calculates time units which are – hrs, mins and secs
     *
     * @param {Number} timestampDiff between futureTime and currentTime timestamps
     * @returns {Object} this.unitObj an object with hours, minutes and seconds for timestampDiff
     */
    Chronos.calculateUnits = function calculateUnits(timestampDiff) {

        var hoursRaw,
            hoursRemains,
            minutesRaw,
            minutesRemains,
            secondsRaw;

        this.unitObj = {};

        hoursRaw = timestampDiff / 1000 / 3600;

        this.unitObj.hours = Math.floor(hoursRaw);

        hoursRemains = hoursRaw - this.unitObj.hours;

        minutesRaw = hoursRemains * 60;

        this.unitObj.minutes = Math.floor(minutesRaw);

        minutesRemains = minutesRaw - this.unitObj.minutes;

        secondsRaw = minutesRemains * 60;

        this.unitObj.seconds = Math.floor(secondsRaw);

        return this.unitObj;

    };

    /**
     * @method adjustUnits
     * Converts time units to human readable format
     *
     * @returns {Object} this the this object for chaining
     */
    Chronos.adjustUnits = function adjustUnits() {

        var props, unitObj;

        props = Object.keys(this.unitObj);

        unitObj = this.unitObj;

        props.forEach(function (prop) {

            unitObj[prop] = (unitObj[prop]).toString();

            if (unitObj[prop].length < 2) {
                unitObj[prop] = '0' + unitObj[prop];
            }
        });

        return this;

    };

    /**
     * @method composeString
     * Converts unit object into string
     *
     * @returns {String} this.composedString a converted string
     */
    Chronos.composeString = function composeString() {

        this.composedString = this.unitObj.hours + ':' + this.unitObj.minutes + ':' + this.unitObj.seconds;

        return this.composedString;

    };

    /**
     * @method composedToArray
     * Converts composed string to an array
     *
     * @param {String} composedString
     * @returns {Array} this.composedArray a converted array
     */
    Chronos.composedToArray = function composedToArray(composedString) {

        if (this.composedString) {
            this.composedArray = this.composedString.split(':');
        } else {
            this.composedArray = this.composeString().split(':');
        }

        return this.composedArray;

    };

    /**
     * @method controller
     * Manage Chronos logic
     *
     * @returns {Object} this Chronos object for chaining
     */
    Chronos.controller = function controller(callback, name) {

        var diff, time1, time2;

        time1 = this.getCurrentTime().getTime();

        if (this.direction === 'forward') {
            time2 = this.getPastTime().getTime();

        } else {

            time2 = time1;
            time1 = this.getFutureTime().getTime();

        }

        diff = this.getDiff(time1, time2);

        this.calculateUnits(diff);

        this.adjustUnits();

        if (callback) {
            callback(this);
        }

        if (name) {
            this.interval = setInterval(this[name].bind(this, callback), 1000);
        }

        return this;

    };

    /**
     * @method start
     * Module entry point
     *
     * @param {Object} params object with initial settings
     * @returns {Object} this Chronos object for chaining
     */
    Chronos.start = function start(params) {

        this.direction = params.direction;

        if (params.timeString) {
            this.timeString = params.timeString;
        }

        this.controller(params.callback, 'controller');

        return this;

    };

    /**
     * @method stop
     * Stops chronos
     *
     * @returns {Object} this Chronos object for chaining
     */
    Chronos.stop = function stop() {

        clearInterval(this.interval);

        return this;
    };

    return Chronos;
};

if (typeof define === 'function') {

    define(ChronosWrapper);

}<commit_msg>case when backwards time is past fix<commit_after>/*
 * chronos.js
 *
 * Created by happyCoda on 20.10.2015.
 * Copyright (c) 2015 happyCoda. All rights reserved.
 */

var ChronosWrapper = function ChronosWrapper() {

    'use strict';

    /**
     * A module for creating timers.
     * @exports Chronos
     */
    var Chronos = {};

    /**
     * @method get
     * Factory method that returns any other property of Chronos
     *
     * @param {String} prop any string
     * @returns {*} this[prop] any property value or undefined
     */
    Chronos.get = function get(prop) {
        // TODO: make this method to return only string, array or object represents calculated time
        return this[prop];
    };

    /**
     * @method getCurrentTime
     * Gets current time
     *
     * @returns {Object} currentTime current time represented as a Date object
     */
    Chronos.getCurrentTime = function getCurrentTime() {

        var currentTime = new Date();

        return currentTime;

    };

    /**
     * @method getFutureTime
     * Gets future time for backwards count
     *
     * @returns {Object} this.futureTime future time represented as a Date object
     */
    Chronos.getFutureTime = function getFutureTime() {

        this.futureTime = new Date(this.timeString);

        return this.futureTime;
    };

    /**
     * @method getPastTime
     * Gets past time for forward count
     *
     * @returns {Object} this.pastTime past time represented as a Date object
     */
    Chronos.getPastTime = function getPastTime() {
        var currentDateString;

        currentDateString = this.getCurrentTime().toDateString();

        this.pastTime = new Date(currentDateString + ' 00:00');

        return this.pastTime;
    };

    /**
     * @method getDiff
     * Calculates a difference between current and target time
     *
     * @param {Number} time1 timestamp from current or future time
     * @param {Number} time2 timestamp from past or current time
     * @returns {Number} diff difference between current and target timestamps
     */
    Chronos.getDiff = function getDiff(time1, time2) {

        var diff;

        diff = time1 - time2;

        return diff;

    };

    /**
     * @method calculateUnits
     * Calculates time units which are – hrs, mins and secs
     *
     * @param {Number} timestampDiff between futureTime and currentTime timestamps
     * @returns {Object} this.unitObj an object with hours, minutes and seconds for timestampDiff
     */
    Chronos.calculateUnits = function calculateUnits(timestampDiff) {

        var hoursRaw,
            hoursRemains,
            minutesRaw,
            minutesRemains,
            secondsRaw;

        this.unitObj = {};

        hoursRaw = timestampDiff / 1000 / 3600;

        this.unitObj.hours = Math.floor(hoursRaw);

        hoursRemains = hoursRaw - this.unitObj.hours;

        minutesRaw = hoursRemains * 60;

        this.unitObj.minutes = Math.floor(minutesRaw);

        minutesRemains = minutesRaw - this.unitObj.minutes;

        secondsRaw = minutesRemains * 60;

        this.unitObj.seconds = Math.floor(secondsRaw);

        return this.unitObj;

    };

    /**
     * @method adjustUnits
     * Converts time units to human readable format
     *
     * @returns {Object} this the this object for chaining
     */
    Chronos.adjustUnits = function adjustUnits() {

        var props, unitObj;

        props = Object.keys(this.unitObj);

        unitObj = this.unitObj;

        props.forEach(function (prop) {

            unitObj[prop] = (unitObj[prop]).toString();

            if (unitObj[prop].length < 2) {
                unitObj[prop] = '0' + unitObj[prop];
            }
        });

        return this;

    };

    /**
     * @method composeString
     * Converts unit object into string
     *
     * @returns {String} this.composedString a converted string
     */
    Chronos.composeString = function composeString() {

        this.composedString = this.unitObj.hours + ':' + this.unitObj.minutes + ':' + this.unitObj.seconds;

        return this.composedString;

    };

    /**
     * @method composedToArray
     * Converts composed string to an array
     *
     * @param {String} composedString
     * @returns {Array} this.composedArray a converted array
     */
    Chronos.composedToArray = function composedToArray(composedString) {

        if (this.composedString) {
            this.composedArray = this.composedString.split(':');
        } else {
            this.composedArray = this.composeString().split(':');
        }

        return this.composedArray;

    };

    /**
     * @method controller
     * Manage Chronos logic
     *
     * @returns {Object} this Chronos object for chaining
     */
    Chronos.controller = function controller(callback, name) {

        var diff, time1, time2;

        time1 = this.getCurrentTime().getTime();

        if (this.direction === 'forward') {
            time2 = this.getPastTime().getTime();

        } else {

            time2 = time1;
            time1 = this.getFutureTime().getTime();

        }

        diff = this.getDiff(time1, time2);

        if (diff <= 1000) {
            diff = 0;

            if (this.interval) {
                this.stop();
            }

        }

        this.calculateUnits(diff);

        this.adjustUnits();

        if (callback) {
            callback(this);
        }

        if (name && diff !== 0) {
            this.interval = setInterval(this[name].bind(this, callback), 1000);
        }

        return this;

    };

    /**
     * @method start
     * Module entry point
     *
     * @param {Object} params object with initial settings
     * @returns {Object} this Chronos object for chaining
     */
    Chronos.start = function start(params) {

        this.direction = params.direction;

        if (params.timeString) {
            this.timeString = params.timeString;
        }

        this.controller(params.callback, 'controller');

        return this;

    };

    /**
     * @method stop
     * Stops chronos
     *
     * @returns {Object} this Chronos object for chaining
     */
    Chronos.stop = function stop() {

        clearInterval(this.interval);

        return this;
    };

    return Chronos;
};

if (typeof define === 'function') {

    define(ChronosWrapper);

}<|endoftext|>


<commit_before>if (!('console' in window))
  window.console = {log: function () {}};

function getBacktrace() {
  try {
    throw new Error();
  } catch (e) {
    return e.stack;
  }
};

function traceMethodCalls(object, methodName) {
  var original = object[methodName];
  function tracer() {
    var args = $.makeArray(arguments);
    console.log("traceIn:",object,".",methodName,"(",args,")");
    try {
      var rv = original.apply(this, arguments);
      console.log("traceOut: rv = ",rv);
      return rv;
    } catch (e) {
      console.log("traceExc: e = ", e);
      throw e;
    }
  }
  object[methodName] = tracer;
}

function escapeHTML() {
  return String(arguments[0]).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
}

function escapeJS(string) {
  return String(string).replace(/\\/g, '\\\\').replace(/["']/g, '\\$&');
}

function renderJSLink(functionName, arg, prefix) {
  prefix = prefix || "javascript:"
  return escapeHTML(prefix + functionName + "('" + escapeJS(arg) + "')")
}

// Based on: http://ejohn.org/blog/javascript-micro-templating/
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
;(function(){
  var cache = {};

  function handleURLEncodedScriptlets(str) {
    var re = /%7B(?:%25|%)=.+?(?:%25|%)%7D/ig;
    var match;
    var res = [];
    var lastIndex = 0;
    var prematch;
    while ((match = re.exec(str))) {
      prematch = str.substring(lastIndex, match.index);
      if (prematch.length)
        res.push(prematch);
      // firefox can be a bit wrong here, forgetting to escape single '%'
      var matchStr = match[0].replace('%=', '%25=').replace('%%7D', '%25%7D');
      res.push(decodeURIComponent(matchStr));
      lastIndex = re.lastIndex;
    }

    // fastpath
    if (!lastIndex)
      return str;

    prematch = str.substring(lastIndex);
    if (prematch.length)
      res.push(prematch);

    return res.join('');
  }

  this.tmpl = function tmpl(str, data){
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.

    var fn = !/\W/.test(str) && (cache[str] = cache[str] ||
                                 tmpl(document.getElementById(str).innerHTML));

    if (!fn) {
      str = handleURLEncodedScriptlets(str);
      var body = "var p=[],print=function(){p.push.apply(p,arguments);}," +
        "h=window.escapeHTML," +
        "jsLink=window.renderJSLink;" +

      // Introduce the data as local variables using with(){}
      "with(obj){p.push('" +

      // Convert the template into pure JavaScript
      str
      .replace(/[\r\t\n]/g, " ")
      .split("{%").join("\t")
      .replace(/((^|%})[^\t]*)'/g, "$1\r")
      .replace(/\t=(.*?)%}/g, "',$1,'")
      .split("\t").join("');")
      .split("%}").join("p.push('")
      .split("\r").join("\\'")
        + "');}return p.join('');"

      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      fn = new Function("obj", body);
    }

    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
})();

$.isString = function (s) {
  return typeof(s) == "string" || (s instanceof String);
}

var SpinnerHTML = "
Loading...
";

function prepareAreaUpdate(jq) {
  if ($.isString(jq))
    jq = $(jq);
  var height = jq.height();
  var width = jq.width();
  if (height < 50)
    height = 50;
  if (width < 100)
    width = 100;
  var replacement = $(SpinnerHTML, document);
  replacement.css('width', width + 'px').css('height', height + 'px').css('lineHeight', height + 'px');
  jq.html("");
  jq.append(replacement);
}

function getRealBackgroundColor(jq) {
  while (true) {
    if (!jq.length)
      return 'transparent';
    var rv = jq.css('background-color');
    if (rv != 'transparent' && rv != 'inherit')
      return rv;
    jq = jq.parent();
  }
}

function overlayWithSpinner(jq) {
  if ($.isString(jq))
    jq = $(jq);
  var height = jq.height();
  var width = jq.width();
  var html = $(SpinnerHTML, document);
  var pos = jq.position();
  var realBackgroundColor = getRealBackgroundColor(jq);
  var newStyle = {
    width: width+'px',
    height: height+'px',
    lineHeight: height+'px',
    position: 'absolute',
    'background-color': realBackgroundColor,
    'z-level': 9999,
    top: pos.top + 'px',
    left: pos.lext + 'px'
  }
  _.each(newStyle, function (value, key) {
    html.css(key, value);
  })
  jq.after(html);

  return {
    remove: function () {
      html.remove();
    }
  };
}

function prepareRenderTemplate() {
  $.each($.makeArray(arguments), function () {
    prepareAreaUpdate('#'+ this + '_container');
  });
}

var ViewHelpers = {};
var AfterTemplateHooks;

function renderTemplate(key, data) {
  var to = key + '_container';
  var from = key + '_template';
  if ($.isArray(data)) {
    data = {rows:data};
  }

  data = _.extend({V: ViewHelpers}, data);

  var oldHooks = AfterTemplateHooks;
  AfterTemplateHooks = [];
  try {
    $i(to).innerHTML = tmpl(from, data);

    _.each(AfterTemplateHooks, function (hook) {
      hook.call();
    });

    $(window).trigger('template:rendered');
  } finally {
    AfterTemplateHooks = oldHooks;
  }
}

function $m(self, method, klass) {
  if (klass) {
    var f = klass.prototype[method];
    if (!f || !f.apply)
      throw new Error("Bogus method: " + method + " on prototype of: " + klass);
  } else {
    var f = self[method];
    if (!f || !f.apply)
      throw new Error("Bogus method: " + method + " on object: " + self);
  }

  function rv () {
    return f.apply(self, arguments);
  }

  // TODO: this is debugging measure. Disable it later.
  rv['$m'] = _.toArray(arguments);
  rv['$m.f'] = f;

  return rv;
}

function $i(id) {
  return document.getElementById(id);
}

// stolen from MIT-licensed prototype.js http://www.prototypejs.org/
function functionArgumentNames(f) {
  var names = f.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
                                 .replace(/\s+/g, '').split(',');
  return names.length == 1 && !names[0] ? [] : names;
};

function jsComparator(a,b) {
  return (a == b) ? 0 : ((a < b) ? -1 : 1);
}

function reloadApp() {
  prepareAreaUpdate($(document.body));
  window.location.reload();
}

// this thing ensures that a back button pressed during some modal
// action (waiting http POST response, for example) will reload the
// page, so that we don't have to face issues caused by unexpected
// change of state
(function () {
  var modalLevel = 0;

  function ModalAction() {
    modalLevel++;
    this.finish = function () {
      modalLevel--;
      delete this.finish;
    }
  }

  window.ModalAction = ModalAction;

  ModalAction.isActive = function () {
    return modalLevel;
  }

  ModalAction.leavingNow = function () {
    if (modalLevel) {
      reloadApp();
    }
  }

  $(function () {
    // first event is skipped, 'cause it's manually triggered by us
    var hadFirstEvent = false;
    $(window).bind('hashchange', function () {
      if (hadFirstEvent)
        ModalAction.leavingNow();
      else
        hadFirstEvent = true;
    })
  });
})();

function isBlank(e) {
  return e == null || !e.length;
}

(function () {
  var plannedState;
  var timeoutId;

  function planPushState(state) {
    plannedState = state;
    if (!timeoutId) {
      timeoutId = setTimeout(function () {
        $.bbq.pushState(plannedState, 2);
        plannedState = undefined;
        timeoutId = undefined;
      }, 100);
      $(window).trigger('hashchange');
    }
  }
  function setHashFragmentParam(name, value) {
    var state = _.extend({}, plannedState || $.bbq.getState());
    if (value == null) {
      delete state[name];
      planPushState(state);
    } else if (value != state[name]) {
      state[name] = value;
      planPushState(state);
    }
  }

  function getHashFragmentParam(name) {
    return (plannedState && plannedState[name]) || $.bbq.getState(name);
  }

  window.setHashFragmentParam = setHashFragmentParam;
  window.getHashFragmentParam = getHashFragmentParam;
})();

function watchHashParamChange(param, defaultValue, callback) {
  if (!callback) {
    callback = defaultValue;
    defaultValue = undefined;
  }

  var oldValue;
  var firstTime = true;
  $(function () {
    $(window).bind('hashchange', function () {
      var newValue = getHashFragmentParam(param) || defaultValue;
      if (!firstTime && oldValue == newValue)
        return;
      firstTime = false;
      oldValue = newValue;
      return callback.apply(this, [newValue].concat($.makeArray(arguments)));
    });
  });
}

function showDialog(idOrJQ, options) {
  var jq = _.isString(idOrJQ) ? $($i(idOrJQ)) : idOrJQ;
  options = options || {};
  var action = new ModalAction();
  $(jq).jqm({modal:true,
             onHide: function (h) {
               action.finish();
               // prevent closing if modal action is in progress
               if (ModalAction.isActive()) {
                 // copied from jqmodal itself
                 h.w.hide() && h.o && h.o.remove();
                 return showDialog(idOrJQ, options);
               }

               if (options.onHide)
                 options.onHide(idOrJQ);
               // copied from jqmodal itself
               h.w.hide() && h.o && h.o.remove();
             }}).jqmShow();
}
function hideDialog(id) {
  $($i(id)).jqm().jqmHide();
  ModalAction.leavingNow();
}
<commit_msg>fixed handling of nested modal dialogs<commit_after>if (!('console' in window))
  window.console = {log: function () {}};

function getBacktrace() {
  try {
    throw new Error();
  } catch (e) {
    return e.stack;
  }
};

function traceMethodCalls(object, methodName) {
  var original = object[methodName];
  function tracer() {
    var args = $.makeArray(arguments);
    console.log("traceIn:",object,".",methodName,"(",args,")");
    try {
      var rv = original.apply(this, arguments);
      console.log("traceOut: rv = ",rv);
      return rv;
    } catch (e) {
      console.log("traceExc: e = ", e);
      throw e;
    }
  }
  object[methodName] = tracer;
}

function escapeHTML() {
  return String(arguments[0]).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
}

function escapeJS(string) {
  return String(string).replace(/\\/g, '\\\\').replace(/["']/g, '\\$&');
}

function renderJSLink(functionName, arg, prefix) {
  prefix = prefix || "javascript:"
  return escapeHTML(prefix + functionName + "('" + escapeJS(arg) + "')")
}

// Based on: http://ejohn.org/blog/javascript-micro-templating/
// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
;(function(){
  var cache = {};

  function handleURLEncodedScriptlets(str) {
    var re = /%7B(?:%25|%)=.+?(?:%25|%)%7D/ig;
    var match;
    var res = [];
    var lastIndex = 0;
    var prematch;
    while ((match = re.exec(str))) {
      prematch = str.substring(lastIndex, match.index);
      if (prematch.length)
        res.push(prematch);
      // firefox can be a bit wrong here, forgetting to escape single '%'
      var matchStr = match[0].replace('%=', '%25=').replace('%%7D', '%25%7D');
      res.push(decodeURIComponent(matchStr));
      lastIndex = re.lastIndex;
    }

    // fastpath
    if (!lastIndex)
      return str;

    prematch = str.substring(lastIndex);
    if (prematch.length)
      res.push(prematch);

    return res.join('');
  }

  this.tmpl = function tmpl(str, data){
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.

    var fn = !/\W/.test(str) && (cache[str] = cache[str] ||
                                 tmpl(document.getElementById(str).innerHTML));

    if (!fn) {
      str = handleURLEncodedScriptlets(str);
      var body = "var p=[],print=function(){p.push.apply(p,arguments);}," +
        "h=window.escapeHTML," +
        "jsLink=window.renderJSLink;" +

      // Introduce the data as local variables using with(){}
      "with(obj){p.push('" +

      // Convert the template into pure JavaScript
      str
      .replace(/[\r\t\n]/g, " ")
      .split("{%").join("\t")
      .replace(/((^|%})[^\t]*)'/g, "$1\r")
      .replace(/\t=(.*?)%}/g, "',$1,'")
      .split("\t").join("');")
      .split("%}").join("p.push('")
      .split("\r").join("\\'")
        + "');}return p.join('');"

      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      fn = new Function("obj", body);
    }

    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
})();

$.isString = function (s) {
  return typeof(s) == "string" || (s instanceof String);
}

var SpinnerHTML = "
Loading...
";

function prepareAreaUpdate(jq) {
  if ($.isString(jq))
    jq = $(jq);
  var height = jq.height();
  var width = jq.width();
  if (height < 50)
    height = 50;
  if (width < 100)
    width = 100;
  var replacement = $(SpinnerHTML, document);
  replacement.css('width', width + 'px').css('height', height + 'px').css('lineHeight', height + 'px');
  jq.html("");
  jq.append(replacement);
}

function getRealBackgroundColor(jq) {
  while (true) {
    if (!jq.length)
      return 'transparent';
    var rv = jq.css('background-color');
    if (rv != 'transparent' && rv != 'inherit')
      return rv;
    jq = jq.parent();
  }
}

function overlayWithSpinner(jq) {
  if ($.isString(jq))
    jq = $(jq);
  var height = jq.height();
  var width = jq.width();
  var html = $(SpinnerHTML, document);
  var pos = jq.position();
  var realBackgroundColor = getRealBackgroundColor(jq);
  var newStyle = {
    width: width+'px',
    height: height+'px',
    lineHeight: height+'px',
    position: 'absolute',
    'background-color': realBackgroundColor,
    'z-level': 9999,
    top: pos.top + 'px',
    left: pos.lext + 'px'
  }
  _.each(newStyle, function (value, key) {
    html.css(key, value);
  })
  jq.after(html);

  return {
    remove: function () {
      html.remove();
    }
  };
}

function prepareRenderTemplate() {
  $.each($.makeArray(arguments), function () {
    prepareAreaUpdate('#'+ this + '_container');
  });
}

var ViewHelpers = {};
var AfterTemplateHooks;

function renderTemplate(key, data) {
  var to = key + '_container';
  var from = key + '_template';
  if ($.isArray(data)) {
    data = {rows:data};
  }

  data = _.extend({V: ViewHelpers}, data);

  var oldHooks = AfterTemplateHooks;
  AfterTemplateHooks = [];
  try {
    $i(to).innerHTML = tmpl(from, data);

    _.each(AfterTemplateHooks, function (hook) {
      hook.call();
    });

    $(window).trigger('template:rendered');
  } finally {
    AfterTemplateHooks = oldHooks;
  }
}

function $m(self, method, klass) {
  if (klass) {
    var f = klass.prototype[method];
    if (!f || !f.apply)
      throw new Error("Bogus method: " + method + " on prototype of: " + klass);
  } else {
    var f = self[method];
    if (!f || !f.apply)
      throw new Error("Bogus method: " + method + " on object: " + self);
  }

  function rv () {
    return f.apply(self, arguments);
  }

  // TODO: this is debugging measure. Disable it later.
  rv['$m'] = _.toArray(arguments);
  rv['$m.f'] = f;

  return rv;
}

function $i(id) {
  return document.getElementById(id);
}

// stolen from MIT-licensed prototype.js http://www.prototypejs.org/
function functionArgumentNames(f) {
  var names = f.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
                                 .replace(/\s+/g, '').split(',');
  return names.length == 1 && !names[0] ? [] : names;
};

function jsComparator(a,b) {
  return (a == b) ? 0 : ((a < b) ? -1 : 1);
}

function reloadApp() {
  prepareAreaUpdate($(document.body));
  window.location.reload();
}

// this thing ensures that a back button pressed during some modal
// action (waiting http POST response, for example) will reload the
// page, so that we don't have to face issues caused by unexpected
// change of state
(function () {
  var modalLevel = 0;

  function ModalAction() {
    modalLevel++;
    this.finish = function () {
      modalLevel--;
      delete this.finish;
    }
  }

  window.ModalAction = ModalAction;

  ModalAction.isActive = function () {
    return modalLevel;
  }

  ModalAction.leavingNow = function () {
    if (modalLevel) {
      reloadApp();
    }
  }

  $(function () {
    // first event is skipped, 'cause it's manually triggered by us
    var hadFirstEvent = false;
    $(window).bind('hashchange', function () {
      if (hadFirstEvent)
        ModalAction.leavingNow();
      else
        hadFirstEvent = true;
    })
  });
})();

function isBlank(e) {
  return e == null || !e.length;
}

(function () {
  var plannedState;
  var timeoutId;

  function planPushState(state) {
    plannedState = state;
    if (!timeoutId) {
      timeoutId = setTimeout(function () {
        $.bbq.pushState(plannedState, 2);
        plannedState = undefined;
        timeoutId = undefined;
      }, 100);
      $(window).trigger('hashchange');
    }
  }
  function setHashFragmentParam(name, value) {
    var state = _.extend({}, plannedState || $.bbq.getState());
    if (value == null) {
      delete state[name];
      planPushState(state);
    } else if (value != state[name]) {
      state[name] = value;
      planPushState(state);
    }
  }

  function getHashFragmentParam(name) {
    return (plannedState && plannedState[name]) || $.bbq.getState(name);
  }

  window.setHashFragmentParam = setHashFragmentParam;
  window.getHashFragmentParam = getHashFragmentParam;
})();

function watchHashParamChange(param, defaultValue, callback) {
  if (!callback) {
    callback = defaultValue;
    defaultValue = undefined;
  }

  var oldValue;
  var firstTime = true;
  $(function () {
    $(window).bind('hashchange', function () {
      var newValue = getHashFragmentParam(param) || defaultValue;
      if (!firstTime && oldValue == newValue)
        return;
      firstTime = false;
      oldValue = newValue;
      return callback.apply(this, [newValue].concat($.makeArray(arguments)));
    });
  });
}

function showDialog(idOrJQ, options) {
  var jq = _.isString(idOrJQ) ? $($i(idOrJQ)) : idOrJQ;
  options = options || {};
  var onHashChange;

  $(window).bind('hashchange', onHashChange = function () {
    hideDialog(jq);
  });

  jq.jqm({modal:true,
          onHide: function (h) {
            $(window).unbind('hashchange', onHashChange);

            // prevent closing if modal action is in progress
            if (ModalAction.isActive()) {
              // copied from jqmodal itself
              h.w.hide() && h.o && h.o.remove();
              return showDialog(idOrJQ, options);
            }

            if (options.onHide)
              options.onHide(idOrJQ);
            // copied from jqmodal itself
            h.w.hide() && h.o && h.o.remove();
          }}).jqmShow();
}

function hideDialog(id) {
  if (_.isString(id))
    id = $($i(id));
  id.jqm().jqmHide();
  ModalAction.leavingNow();
}
<|endoftext|>


<commit_before>define("ace/mode/nsis_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) {
"use strict";

var oop = require("../lib/oop");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;

var NSISHighlightRules = function() {

    this.$rules = {
        start: [{
            token: "keyword.compiler.nsis",
            regex: /(?:\b|^\s*)\!(?:include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|finalize|getdllversion|system|tempfile|warning|verbose|define|undef|insertmacro|macro|macroend|makensis|searchparse|searchreplace)\b/,
            caseInsensitive: true
        }, {
            token: "keyword.command.nsis",
            regex: /(?:\b|^\s*)(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|IntCmp|IntCmpU|IntFmt|IntOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetPluginUnload|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegStr|WriteUninstaller|XPStyle)\b/,
            caseInsensitive: true
        }, {
            token: "keyword.control.nsis",
            regex: /(?:\b|^\s*)\!(?:ifdef|ifndef|if|ifmacrodef|ifmacrondef|else|endif)\b/,
            caseInsensitive: true
        }, {
            token: "keyword.plugin.nsis",
            regex: /(?:\b|^\s*)\w+\:\:\w+/,
            caseInsensitive: true
        }, {
            token: "keyword.operator.comparison.nsis",
            regex: /[!<>]?=|<>|<|>/
        }, {
            token: "support.function.nsis",
            regex: /(?:\b|^\s*)(?:Function|FunctionEnd|Section|SectionEnd|SectionGroup|SectionGroupEnd|SubSection|SubSectionEnd|PageEx|PageExEnd)\b/,
            caseInsensitive: true
        }, {
            token: "support.library.nsis",
            regex: /\${[\w]+}/
        }, {
            token: "constant.nsis",
            regex: /(?:\b|^\s*)(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/,
            caseInsensitive: true
        }, {
            token: "constant.library.nsis",
            regex: /\${(?:AtLeastServicePack|AtLeastWin7|AtLeastWin8|AtLeastWin10|AtLeastWin95|AtLeastWin98|AtLeastWin2000|AtLeastWin2003|AtLeastWin2008|AtLeastWin2008R2|AtLeastWinME|AtLeastWinNT4|AtLeastWinVista|AtLeastWinXP|AtMostServicePack|AtMostWin7|AtMostWin8|AtMostWin10|AtMostWin95|AtMostWin98|AtMostWin2000|AtMostWin2003|AtMostWin2008|AtMostWin2008R2|AtMostWinME|AtMostWinNT4|AtMostWinVista|AtMostWinXP|IsNT|IsServer|IsServicePack|IsWin7|IsWin8|IsWin10|IsWin95|IsWin98|IsWin2000|IsWin2003|IsWin2008|IsWin2008R2|IsWinME|IsWinNT4|IsWinVista|IsWinXP)}/
        }, {
            token: "constant.language.boolean.true.nsis",
            regex: /\b(?:true|on)\b/
        }, {
            token: "constant.language.boolean.false.nsis",
            regex: /\b(?:false|off)\b/
        }, {
            token: "constant.language.option.nsis",
            regex: /(?:\b|^\s*)(?:(?:un\.)?components|(?:un\.)?custom|(?:un\.)?directory|(?:un\.)?instfiles|(?:un\.)?license|uninstConfirm|admin|all|auto|both|bottom|bzip2|current|force|hide|highest|ifdiff|ifnewer|lastused|leave|left|listonly|lzma|nevershow|none|normal|notset|right|show|silent|silentlog|textonly|top|try|user|Win10|Win7|Win8|WinVista|zlib)\b/,
            caseInsensitive: true
        }, {
            token: "constant.language.slash-option.nsis",
            regex: /\/(?:a|BRANDING|CENTER|COMPONENTSONLYONCUSTOM|CUSTOMSTRING\=|date|e|ENABLECANCEL|FILESONLY|file|FINAL|GLOBAL|gray|ifempty|ifndef|ignorecase|IMGID\=|ITALIC|LANG\=|NOCUSTOM|noerrors|NONFATAL|nonfatal|oname\=|o|REBOOTOK|redef|RESIZETOFIT|r|SHORT|SILENT|SOLID|STRIKE|TRIM|UNDERLINE|utcdate|windows|x)\b/,
            caseInsensitive: true
        }, {
            token: "constant.numeric.nsis",
            regex: /\b(?:0(?:x|X)[0-9a-fA-F]+|[0-9]+(?:\.[0-9]+)?)\b/
        }, {
            token: "entity.name.function.nsis",
            regex: /\${[\w]+}/
        }, {
            token: "storage.type.function.nsis",
            regex: /\$[\w]+/
        }, {
            token: "punctuation.definition.string.begin.nsis",
            regex: /`/,
            push: [{
                token: "punctuation.definition.string.end.nsis",
                regex: /`/,
                next: "pop"
            }, {
                token: "constant.character.escape.nsis",
                regex: /\$\\./
            }, {
                defaultToken: "string.quoted.back.nsis"
            }]
        }, {
            token: "punctuation.definition.string.begin.nsis",
            regex: /"/,
            push: [{
                token: "punctuation.definition.string.end.nsis",
                regex: /"/,
                next: "pop"
            }, {
                token: "constant.character.escape.nsis",
                regex: /\$\\./
            }, {
                defaultToken: "string.quoted.double.nsis"
            }]
        }, {
            token: "punctuation.definition.string.begin.nsis",
            regex: /'/,
            push: [{
                token: "punctuation.definition.string.end.nsis",
                regex: /'/,
                next: "pop"
            }, {
                token: "constant.character.escape.nsis",
                regex: /\$\\./
            }, {
                defaultToken: "string.quoted.single.nsis"
            }]
        }, {
            token: [
                "punctuation.definition.comment.nsis",
                "comment.line.nsis"
            ],
            regex: /(;|#)(.*$)/
        }, {
            token: "punctuation.definition.comment.nsis",
            regex: /\/\*/,
            push: [{
                token: "punctuation.definition.comment.nsis",
                regex: /\*\//,
                next: "pop"
            }, {
                defaultToken: "comment.block.nsis"
            }]
        }, {
            token: "text",
            regex: /(?:\!include|\!insertmacro)\b/
        }]
    };

    this.normalizeRules();
};

NSISHighlightRules.metaData = {
    comment: "\n\ttodo: - highlight functions\n\t",
    fileTypes: ["nsi", "nsh"],
    name: "NSIS",
    scopeName: "source.nsis"
};

oop.inherits(NSISHighlightRules, TextHighlightRules);

exports.NSISHighlightRules = NSISHighlightRules;
});

define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(require, exports, module) {
"use strict";

var oop = require("../../lib/oop");
var Range = require("../../range").Range;
var BaseFoldMode = require("./fold_mode").FoldMode;

var FoldMode = exports.FoldMode = function(commentRegex) {
    if (commentRegex) {
        this.foldingStartMarker = new RegExp(
            this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
        );
        this.foldingStopMarker = new RegExp(
            this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
        );
    }
};
oop.inherits(FoldMode, BaseFoldMode);

(function() {

    this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
    this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
    this.singleLineBlockCommentRe= /^\s*(\/\*).*\*\/\s*$/;
    this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
    this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
    this._getFoldWidgetBase = this.getFoldWidget;
    this.getFoldWidget = function(session, foldStyle, row) {
        var line = session.getLine(row);

        if (this.singleLineBlockCommentRe.test(line)) {
            if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
                return "";
        }

        var fw = this._getFoldWidgetBase(session, foldStyle, row);

        if (!fw && this.startRegionRe.test(line))
            return "start"; // lineCommentRegionStart

        return fw;
    };

    this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) {
        var line = session.getLine(row);

        if (this.startRegionRe.test(line))
            return this.getCommentRegionBlock(session, line, row);

        var match = line.match(this.foldingStartMarker);
        if (match) {
            var i = match.index;

            if (match[1])
                return this.openingBracketBlock(session, match[1], row, i);

            var range = session.getCommentFoldRange(row, i + match[0].length, 1);

            if (range && !range.isMultiLine()) {
                if (forceMultiline) {
                    range = this.getSectionRange(session, row);
                } else if (foldStyle != "all")
                    range = null;
            }

            return range;
        }

        if (foldStyle === "markbegin")
            return;

        var match = line.match(this.foldingStopMarker);
        if (match) {
            var i = match.index + match[0].length;

            if (match[1])
                return this.closingBracketBlock(session, match[1], row, i);

            return session.getCommentFoldRange(row, i, -1);
        }
    };

    this.getSectionRange = function(session, row) {
        var line = session.getLine(row);
        var startIndent = line.search(/\S/);
        var startRow = row;
        var startColumn = line.length;
        row = row + 1;
        var endRow = row;
        var maxRow = session.getLength();
        while (++row < maxRow) {
            line = session.getLine(row);
            var indent = line.search(/\S/);
            if (indent === -1)
                continue;
            if  (startIndent > indent)
                break;
            var subRange = this.getFoldWidgetRange(session, "all", row);

            if (subRange) {
                if (subRange.start.row <= startRow) {
                    break;
                } else if (subRange.isMultiLine()) {
                    row = subRange.end.row;
                } else if (startIndent == indent) {
                    break;
                }
            }
            endRow = row;
        }

        return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
    };
    this.getCommentRegionBlock = function(session, line, row) {
        var startColumn = line.search(/\s*$/);
        var maxRow = session.getLength();
        var startRow = row;

        var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
        var depth = 1;
        while (++row < maxRow) {
            line = session.getLine(row);
            var m = re.exec(line);
            if (!m) continue;
            if (m[1]) depth--;
            else depth++;

            if (!depth) break;
        }

        var endRow = row;
        if (endRow > startRow) {
            return new Range(startRow, startColumn, endRow, line.length);
        }
    };

}).call(FoldMode.prototype);

});

define("ace/mode/nsis",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/nsis_highlight_rules","ace/mode/folding/cstyle"], function(require, exports, module) {
"use strict";

var oop = require("../lib/oop");
var TextMode = require("./text").Mode;
var NSISHighlightRules = require("./nsis_highlight_rules").NSISHighlightRules;
var FoldMode = require("./folding/cstyle").FoldMode;

var Mode = function() {
    this.HighlightRules = NSISHighlightRules;
    this.foldingRules = new FoldMode();
};
oop.inherits(Mode, TextMode);

(function() {
    this.lineCommentStart = [";", "#"];
    this.blockComment = {start: "/*", end: "*/"};
    this.$id = "ace/mode/nsis";
}).call(Mode.prototype);

exports.Mode = Mode;
});
<commit_msg>Update public/libs/bower_components/ace-builds/src/mode-nsis.js<commit_after><|endoftext|>


<commit_before>// (C) Copyright 2014-2015 Hewlett Packard Enterprise Development LP

var React = require('react');
var DocsArticle = require('../../DocsArticle');
var Video = require('grommet/components/Video');
var A11y = require('../../utils/a11y');

var VideoDoc = React.createClass({

  getInitialState: function () {
    return {
      singleSelection: [0]
    };
  },

  componentDidMount: function () {
    A11y.updatePageTitle('Video');
  },

  // single selection is managed by the caller via state.singleSelection
  _onSingleSelect: function (selection) {
    this.setState({singleSelection: selection});
  },

  // multiple selection is managed by the Table
  _onMultipleSelect: function (selection) {
    // no-op
  },

  render: function() {
    var inline = [
      "<Video>",
      "  <source src=\"video/file.mp4\" type='video/mp4'/>",
      "</Video>"
    ].join('\n');

    return (
      <DocsArticle title="Video" colorIndex="neutral-3">

Video built on HTML5 video element. Callers must include
          child <source>elements according to the
          HTML5 <video> specification.

{inline}

Options

allowFullScreen   true|false

Enables fullscreen/expand control button on player.

autoPlay   true|false

Enables automatically playback of the video as soon as it is loaded. Defaults to false.

colorIndex   {"{category}-{index}"}

The color identifier to use for the background color.
              For example: "neutral-1". This is visible when a
              poster image is not the same aspect ratio as the video.

duration     number

The length of the video in seconds.

full         true|false

Whether the width should take the full container width.

onClick      {"{func}"}

Click handler to override the default click handler to play video and pass in a different function instead.

loop       true|false

Enables video looping continuously. Defaults to false.

muted       true|false

Enables video muting. This option is best used with the autoPlay flag. Defaults to false.

poster       {"{url}"}

Poster image to show before the video first plays.

size         small|medium|large

The width of the Video. Defaults to medium unless
              the full option is specified.
              The height will adapt to the aspect ratio of the video.

showControls         true|false

Show controls such as play button, progress bar, etc. on top of video. Defaults to true.

timeline     {"[{...}]"}

An array of:
              {"{label: <string>, seconds: <number>}"}
               used to indicate chapter markers.

title        {"{string}|{node}"}

Descriptive title.

videoHeader        {"{node}"}

For instances when the video and header components are used together.

Examples

Simple

            <Video>
              <source src="video/test.mp4" type='video/mp4'/>
            </Video>

{"<Video>\n" +
            "  <source src=\"video/test.mp4\" type=\"video/mp4\"/>\n" +
            "</Video>"}

Title, Duration, Timeline, Poster, Full

            <Video title="Title" poster="img/HTML5.png" full={true}
              duration={35} timeline={[
                {label: 'Chapter 1', time: 0},
                {label: 'Chapter 2', time: 10},
                {label: 'Chapter 3', time: 20}
              ]}>
              <source src="video/test.mp4" type="video/mp4"/>
            </Video>

{"<Video\n" +
            "  title=\"Title\" poster=\"img/HTML5.png\" full={true}" +
            "  duration={35} timeline={[\n" +
            "    {label: \"Chapter 1\", time: 0},\n" +
            "    {label: \"Chapter 2\", time: 10},\n" +
            "    {label: \"Chapter 3\", time: 20}\n" +
            "  ]}>\n" +
            "  <source src=\"video/test.mp4\" type=\"video/mp4\"/>\n" +
            "</Video>"}

Small

            <Video size="small">
              <source src="video/test.mp4" type="video/mp4"/>
            </Video>

Large

            <Video size="large">
              <source src="video/test.mp4" type="video/mp4"/>
            </Video>

      </DocsArticle>
    );
  }
});

module.exports = VideoDoc;
<commit_msg>Fixed Video component's description for options.<commit_after>// (C) Copyright 2014-2015 Hewlett Packard Enterprise Development LP

var React = require('react');
var DocsArticle = require('../../DocsArticle');
var Video = require('grommet/components/Video');
var A11y = require('../../utils/a11y');

var VideoDoc = React.createClass({

  getInitialState: function () {
    return {
      singleSelection: [0]
    };
  },

  componentDidMount: function () {
    A11y.updatePageTitle('Video');
  },

  // single selection is managed by the caller via state.singleSelection
  _onSingleSelect: function (selection) {
    this.setState({singleSelection: selection});
  },

  // multiple selection is managed by the Table
  _onMultipleSelect: function (selection) {
    // no-op
  },

  render: function() {
    var inline = [
      "<Video>",
      "  <source src=\"video/file.mp4\" type='video/mp4'/>",
      "</Video>"
    ].join('\n');

    return (
      <DocsArticle title="Video" colorIndex="neutral-3">

Video built on HTML5 video element. Callers must include
          child <source>elements according to the
          HTML5 <video> specification.

{inline}

Options

allowFullScreen   true|false

Enables fullscreen/expand control button on player.

autoPlay   true|false

Enables automatic playback of the video as soon as it is loaded. Defaults to false.

colorIndex   {"{category}-{index}"}

The color identifier to use for the background color.
              For example: "neutral-1". This is visible when a
              poster image is not the same aspect ratio as the video.

duration     number

The length of the video in seconds.

full         true|false

Whether the width should take the full container width.

onClick      {"{func}"}

Click handler to override the default click handler to play video and pass in a different function instead.

loop       true|false

Enables continuous video looping. Defaults to false.

muted       true|false

Enables video muting. This option is best used with the autoPlay flag. Defaults to false.

poster       {"{url}"}

Poster image to show before the video first plays.

size         small|medium|large

The width of the Video. Defaults to medium unless
              the full option is specified.
              The height will adapt to the aspect ratio of the video.

showControls         true|false

Show controls such as play button, progress bar, etc. on top of video. Defaults to true.

timeline     {"[{...}]"}

An array of:
              {"{label: <string>, seconds: <number>}"}
               used to indicate chapter markers.

title        {"{string}|{node}"}

Descriptive title.

videoHeader        {"{node}"}

For instances when the video and header components are used together.

Examples

Simple

            <Video>
              <source src="video/test.mp4" type='video/mp4'/>
            </Video>

{"<Video>\n" +
            "  <source src=\"video/test.mp4\" type=\"video/mp4\"/>\n" +
            "</Video>"}

Title, Duration, Timeline, Poster, Full

            <Video title="Title" poster="img/HTML5.png" full={true}
              duration={35} timeline={[
                {label: 'Chapter 1', time: 0},
                {label: 'Chapter 2', time: 10},
                {label: 'Chapter 3', time: 20}
              ]}>
              <source src="video/test.mp4" type="video/mp4"/>
            </Video>

{"<Video\n" +
            "  title=\"Title\" poster=\"img/HTML5.png\" full={true}" +
            "  duration={35} timeline={[\n" +
            "    {label: \"Chapter 1\", time: 0},\n" +
            "    {label: \"Chapter 2\", time: 10},\n" +
            "    {label: \"Chapter 3\", time: 20}\n" +
            "  ]}>\n" +
            "  <source src=\"video/test.mp4\" type=\"video/mp4\"/>\n" +
            "</Video>"}

Small

            <Video size="small">
              <source src="video/test.mp4" type="video/mp4"/>
            </Video>

Large

            <Video size="large">
              <source src="video/test.mp4" type="video/mp4"/>
            </Video>

      </DocsArticle>
    );
  }
});

module.exports = VideoDoc;
<|endoftext|>


<commit_before>var DomainObject = require('./DomainObject');

/**
 * It is expected that container is a d3
 * object, where the node will be appended.
 */
var Node = function (container) {

  // a type reference
  this._type = 'Node';

  // initialize the group element
  this.group = container.append('g');
  this.group
    .classed('node', true);

  // append the circle
  this.circle = this.group
    .append('circle')
    .attr('fill', '#ccc')
    .attr('r', '20')
    .attr('stroke', '#aaa')
    .attr('stroke-width', '0')
    .style('cursor', 'pointer');

  // append the text
  this.text = this.group
    .append('text')
    .attr('fill', '#aaa')
    .attr('text-anchor', 'middle')
    .style('font-size', '20px')
    .style('cursor', 'pointer');
};

// Inherit from Domain Object
Node.prototype = new DomainObject();
Node.prototype.constructor = Node;

/**
 * Sets the coordinates of the node
 */
Node.prototype.setXY = function (x, y) {
  this.circle
    .attr('cx', x)
    .attr('cy', y);

  this.text
    .attr('x', x)
    .attr('y', y + 8);
};

module.exports = Node;<commit_msg>save coordinates<commit_after>var DomainObject = require('./DomainObject');

/**
 * It is expected that container is a d3
 * object, where the node will be appended.
 */
var Node = function (container) {

  // a type reference
  this._type = 'Node';

  // initialize the group element
  this.group = container.append('g');
  this.group
    .classed('node', true);

  // append the circle
  this.circle = this.group
    .append('circle')
    .attr('fill', '#ccc')
    .attr('r', '20')
    .attr('stroke', '#aaa')
    .attr('stroke-width', '0')
    .style('cursor', 'pointer');

  // append the text
  this.text = this.group
    .append('text')
    .attr('fill', '#aaa')
    .attr('text-anchor', 'middle')
    .style('font-size', '20px')
    .style('cursor', 'pointer');
};

// Inherit from Domain Object
Node.prototype = new DomainObject();
Node.prototype.constructor = Node;

/**
 * Sets the coordinates of the node
 */
Node.prototype.setXY = function (x, y) {
  this.circle
    .attr('cx', x)
    .attr('cy', y);

  this.text
    .attr('x', x)
    .attr('y', y + 8);

  this.x = x;
  this.y = y;
};

module.exports = Node;<|endoftext|>


<commit_before>'use strict';

var _sa = _sa || {};

_sa.events = [];
_sa.sendInterval = 5000;

_sa.dataUrl ='http://sextant-ng-b.herokuapp.com/api/0_0_1/data';
_sa.keysUrl ='http://sextant-ng-b.herokuapp.com/api/0_0_1/provisionKeys';

// For AJAX
_sa.createXHR = function() {
  try {
    return new XMLHttpRequest();
  } catch(e) {
    throw new Error('No XHR object.');
  }
};

// Execute a POST request
_sa.post = function(url, data, callback) {
  var xhr = this.createXHR();

  xhr.open('POST', url, true);
  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200 && callback) {
      callback(xhr.responseText);
    }
  };

  xhr.send(JSON.stringify(data));
};

// Stamp the request body with credentials
_sa.send= function(url, data, callback) {

  if(!_sa.writeKey && url !== _sa.keysUrl) {
    clearInterval(_sa.sendEvents);
    throw new Error('No write key.');
  }

  var message = {};

  message.uniqueID = _sa.uuid || localStorage.getItem('uuid');
  message.sessionID = _sa.sid;
  message.writeKey = _sa.writeKey;
  message.events = data;

  _sa.post(url, message, callback);
};

// Trim event down to desired information
_sa.processEvent = function(e) {

  // Desired event attributes
  var eventProps = ['type', 'timeStamp'];

  // Desired event target attributes
  var targetProps = ['nodeName', 'innerHTML'];

  // Trimmed event
  var trimmed = {};

  // Only save the whitelisted attributes
  eventProps.forEach(function(prop) {
    if (e[prop]) { trimmed[prop] = e[prop]; }
  });

  // If the event had a target save its attributes too
  if(e.target) {
    targetProps.forEach(function(prop) {
      if(e.target[prop]) { trimmed[prop] = e.target[prop]; }
    });
  }

  return trimmed;
};

// Automatically send any collected events
_sa.sendEvents = setInterval(function() {
  if (!_sa.events.length) return;

  _sa.send(_sa.dataUrl, _sa.events);

  _sa.events = [];
}, _sa.sendInterval);

_sa.angularListener = function() {
  if(!window.angular) return;

  // HTML element where ng-app is defined
  var ngAppNode = document.getElementsByClassName('ng-scope')[0];
  console.log('ngAppNode: ' + ngAppNode);

   // Convert the element into an Angular element to access the $rootScope
  var ngApp = angular.element(ngAppNode);
  console.log('ngApp: ' + ngApp);

  // Listen to route changes on the $routeProvider
  ngApp.scope().$on('$routeChangeSuccess', function(e, current, previous) {
    if (previous) {
      // Navigation between two angular routes
      if(previous.originalPath && current.originalPath) {
        var pageChange = {};

        pageChange.timeStamp = new Date().getTime();
        pageChange.from = previous.originalPath;
        pageChange.to = current.originalPath;

        _sa.events.push(pageChange);
      }
    }
  });
};

// Capture and process all clicks
window.addEventListener('click', function(e) {
  e = event || window.event;

  _sa.events.push(_sa.processEvent(e));
});

// Handle initial page load
// window.addEventListener('load', function() {

(function() {
  var pageLoad = {};

  pageLoad.timeStamp = new Date().getTime();
  pageLoad.page = window.parent.location.href;

  // Get a UUID (if needed), session id, and write key
  _sa.send(_sa.keysUrl, pageLoad, function(responseText) {

    var response = JSON.parse(responseText);

    if (response.uniqueID) {
        localStorage.setItem('uuid', response.uniqueID);
    }

    _sa.uuid = localStorage.getItem('uuid') || response.uniqueID;
    _sa.sid = response.sessionID;
    _sa.writeKey = response.writeKey;
  });

  setTimeout(_sa.angularListener, 1000);

  // _sa.angularListener();
}());

window.addEventListener('load', function() {
  console.log('page load registered!');
  console.log(document.getElementsByClassName('ng-scope')[0]);
});<commit_msg>Send a synchronous request on beforeunload to capture the page close event<commit_after>'use strict';

var _sa = _sa || {};

_sa.events = [];
_sa.sendInterval = 5000;

_sa.dataUrl ='http://sextant-ng-b.herokuapp.com/api/0_0_1/data';
_sa.keysUrl ='http://sextant-ng-b.herokuapp.com/api/0_0_1/provisionKeys';

// For AJAX
_sa.createXHR = function() {
  try {
    return new XMLHttpRequest();
  } catch(e) {
    throw new Error('No XHR object.');
  }
};

// Execute a POST request
_sa.post = function(url, data, callback, async) {
  var xhr = this.createXHR();

  xhr.open('POST', url, typeof async === 'undefined' ? true : false);
  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200 && callback) {
      callback(xhr.responseText);
    }
  };

  xhr.send(JSON.stringify(data));
};

// Stamp the request body with credentials before posting
_sa.send= function(url, data, callback, async) {

  if(!_sa.writeKey && url !== _sa.keysUrl) {
    clearInterval(_sa.sendEvents);
    throw new Error('No write key.');
  }

  var message = {};

  message.uniqueID = _sa.uuid || localStorage.getItem('uuid');
  message.sessionID = _sa.sid;
  message.writeKey = _sa.writeKey;
  message.events = data;

  _sa.post(url, message, callback, async);
};

// Trim event down to desired information
_sa.processEvent = function(e) {

  // Desired event attributes
  var eventProps = ['type', 'timeStamp'];

  // Desired event target attributes
  var targetProps = ['nodeName', 'innerHTML'];

  // Trimmed event
  var trimmed = {};

  // Only save the whitelisted attributes
  eventProps.forEach(function(prop) {
    if (e[prop]) { trimmed[prop] = e[prop]; }
  });

  // If the event had a target save its attributes too
  if(e.target) {
    targetProps.forEach(function(prop) {
      if(e.target[prop]) { trimmed[prop] = e.target[prop]; }
    });
  }

  return trimmed;
};

// Automatically send any collected events
_sa.sendEvents = setInterval(function() {
  if (!_sa.events.length) return;

  _sa.send(_sa.dataUrl, _sa.events);

  _sa.events = [];
}, _sa.sendInterval);

_sa.angularListener = function() {
  if(!window.angular) return;

  // HTML element where ng-app is defined
  var ngAppNode = document.getElementsByClassName('ng-scope')[0];
  console.log('ngAppNode: ' + ngAppNode);

   // Convert the element into an Angular element to access the $rootScope
  var ngApp = angular.element(ngAppNode);
  console.log('ngApp: ' + ngApp);

  // Listen to route changes on the $routeProvider
  ngApp.scope().$on('$routeChangeSuccess', function(e, current, previous) {
    if (previous) {
      // Navigation between two angular routes
      if(previous.originalPath && current.originalPath) {
        var pageChange = {};

        pageChange.timeStamp = new Date().getTime();
        pageChange.from = previous.originalPath;
        pageChange.to = current.originalPath;

        _sa.events.push(pageChange);
      }
    }
  });
};

// Capture and process all clicks
window.addEventListener('click', function(e) {
  e = event || window.event;

  _sa.events.push(_sa.processEvent(e));
});

// Handle initial page load
// window.addEventListener('load', function() {

(function() {
  var pageLoad = {};

  pageLoad.timeStamp = new Date().getTime();
  pageLoad.page = window.parent.location.href;

  // Get a UUID (if needed), session id, and write key
  _sa.send(_sa.keysUrl, pageLoad, function(responseText) {

    var response = JSON.parse(responseText);

    if (response.uniqueID) {
        localStorage.setItem('uuid', response.uniqueID);
    }

    _sa.uuid = localStorage.getItem('uuid') || response.uniqueID;
    _sa.sid = response.sessionID;
    _sa.writeKey = response.writeKey;
  });

  setTimeout(_sa.angularListener, 1000);

  // _sa.angularListener();
}());

window.addEventListener('load', function() {
  console.log('page load registered!');
  console.log(document.getElementsByClassName('ng-scope')[0]);
});

// Send beforeunload event along with any prior events before the page closes
window.addEventListener('beforeunload', function( e ) {

  clearInterval(_sa.sendEvents);

  _sa.events.push(_sa.processEvent(e));

  _sa.send(_sa.dataUrl, _sa.events, null, false);
});<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import Link from '../../components/Link';
import Layout from '../../components/Layout';
import RecipeContents from '../../components/ListView/RecipeContents';
import ComponentInputs from '../../components/ListView/ComponentInputs';
import ComponentDetailsView from '../../components/ListView/ComponentDetailsView';
import CreateComposition from '../../components/Modal/CreateComposition';
import EmptyState from '../../components/EmptyState/EmptyState';
import Toolbar from '../../components/Toolbar/Toolbar';
import constants from '../../core/constants';
import RecipeApi from '../../data/RecipeApi';
import MetadataApi from '../../data/MetadataApi';

class EditRecipePage extends React.Component {

  state = { recipe: {},
            recipeComponents: [], recipeDependencies: [],
            inputComponents: [], inputFilters: [], filteredComponents: [],
            selectedComponent: "", selectedComponentStatus: "", selectedComponentParent: "",
          };

  componentDidMount() {
    document.title = 'Welder | Recipe';
  }

  componentWillMount() {
    // get recipe, get inputs; then update inputs
      let recipeName = this.props.route.params.recipe.replace(/\s/g , "-");
      Promise.all([RecipeApi.getRecipe(recipeName), this.getInputs()]).then((data) => {
          let recipe = {
            "name": data[0].name,
            "description" : data[0].description
          };
          this.setState({recipe: recipe});
          this.setState({recipeComponents: data[0].components});
          this.setState({recipeDependencies: data[0].dependencies});
          // Recipes and available components both need to be updated before running this
          let inputs = this.updateInputs(data[1]);
          this.setState({inputComponents: inputs});

      }).catch(e => console.log('Error in EditRecipe promise: ' + e));
  }

  getInputs(filter){
    // for now, this gets full metadata and dependencies of list that's returned
    // but ideally the returned list would provide only what's needed to display
    // in the list and the popover, then the remaining data would be fetched on
    // Add or View Details
    filter = (filter === undefined) ? "" : filter;
      let p = new Promise((resolve, reject) => {
          // /modules/list looks like:
          // {"modules":[{"name":"389-ds-base","group_type":"rpm"},{"name":"389-ds-base-libs","group_type":"rpm"}, ...]}
          fetch(constants.get_modules_list + filter)
          .then(r => r.json())
          .then(data => {
              let components = data.modules
              let componentNames = MetadataApi.getNames(components);
              Promise.all([
                  MetadataApi.getData(constants.get_module_info + componentNames),
                  MetadataApi.getData(constants.get_dependencies_list + componentNames)
              ]).then((data) => {
                components = MetadataApi.updateComponentMetadata(components, data[0], true);
                components = MetadataApi.updateComponentDependencies(components, data[1]);
                components.map(i => {i.ui_type = "RPM"}) // this is being set arbitrarily for now
                resolve(components);
              }).catch(e => console.log('Error getting recipe metadata: ' + e));
          })
          .catch(e => {
              console.log("Failed to get inputs during recipe edit: " + e);
              reject();
          });
      });
      return p;
  };

  updateInputs(inputs) {
    // add a property to the original set of inputs, that indicates whether the
    // input is in the recipe or not
    let selected = this.state.recipeComponents;
    selected.map(component => {
      let index = inputs.map(input => {return input.name}).indexOf(component.name);
      if (index >= 0) {
          inputs[index].inRecipe = true;
      }
    });
    // this.setState({inputComponents: inputs});
    return inputs;
  };

  getFilteredInputs(event) {
    if (event.which == 13 || event.keyCode == 13) {
      let filter = [{
        "field": "name",
        "value": event.target.value
      }];
      Promise.all([this.getInputs("/*" + filter[0].value + "*")]).then((data) => {
        let inputs = this.updateInputs(data[0]);
        this.setState({filteredComponents : inputs});
        this.setState({inputFilters : filter});
        // this.updateInputs(data[0]); TODO this should be used and refactored to work with any state value
      }).catch(e => console.log('Failed to filter inputs during recipe edit: ' + e));
      event.preventDefault();
    }
  }

  handleClearFilters() {
    this.setState({filteredComponents : []});
    this.setState({inputFilters : []});
    $('#cmpsr-recipe-input-filter').val("");
  }

  clearInputAlert() {
    $("#cmpsr-recipe-inputs .alert").remove();
  }

  handleAddComponent = (event, component, version) => {
    // the user clicked Add in the sidebar to add the component to the recipe

    // get metadata and dependencies for the component
    Promise.all([
        MetadataApi.getMetadataComponent(component, version)
    ]).then((data) => {
      let recipeComponents = this.state.recipeComponents.slice(0);
      let updatedRecipeComponents = recipeComponents.concat(data[0][0]);
      this.setState({recipeComponents: updatedRecipeComponents});
      let recipeDependencies = this.state.recipeDependencies;
      this.setState({recipeDependencies: recipeDependencies.concat(data[0][1])});
      RecipeApi.updateRecipe(data[0][0], "add");
    }).catch(e => console.log('Error getting component metadata: ' + e));

    // if an input is selected, deselect it
    let inputs = this.removeInputActive();

    // set inRecipe to true for the input component
    // in the list of available inputs
    let inputIndex = inputs.map(i => i.name).indexOf(component.name);
    if (inputIndex > -1) {
      inputs[inputIndex].inRecipe = true;
    }
    // and also the list of filtered inputs
    if (this.state.filteredComponents.length > 0) {
      let filteredComponents = this.state.filteredComponents;
      let filteredIndex = filteredComponents.map(i => i.name).indexOf(component.name);
      if (filteredIndex > -1) {
        filteredComponents[filteredIndex].inRecipe = true;
        this.setState({filteredComponents: filteredComponents});
      }
    }
    // TODO if inputs also lists dependencies, should these be indicated as included?
    this.setState({inputComponents: inputs});
    this.setState({selectedComponent: ""});
    this.setState({selectedComponentStatus: ""});
    // remove the inline message above the list of inputs
    this.clearInputAlert();
  };

  handleRemoveComponent = (event, component) => {
    // the user clicked Remove for a component in the recipe component list
    // or the component details view

    // update the recipe object (the one that's used during save)
    RecipeApi.updateRecipe(component, "remove");
    // if the removed component was visible in the details view then close the
    // details view and deselect it in the list of inputs
    let inputs = [];
    if (component == this.state.selectedComponent) {
      inputs = this.removeInputActive();
      this.hideComponentDetails();
    } else {
      inputs = this.state.inputComponents.slice(0);
    }
    // update the list of available components to include the Add button for the
    // removed component
    let inputIndex = inputs.map(i => i.name).indexOf(component.name);
    if (inputIndex > -1) {
      inputs[inputIndex].inRecipe = false;
      this.setState({inputComponents: inputs});
    }
    // update the list of recipe components to not include the removed component
    let updatedRecipeComponents = this.state.recipeComponents.slice(0);
    updatedRecipeComponents = updatedRecipeComponents.filter(obj => (obj !== component));
    this.setState({recipeComponents: updatedRecipeComponents});
  };

  handleComponentDetails = (event, component, parent) => {
    // the user selected a component in the sidebar to view more details on the right
    // remove the active state from the current selected component
    let inputs = this.removeInputActive();
    if (component != this.state.selectedComponent) {
      // if the user did not clicked on the current selected component:
      // set state for selected component
      this.setState({selectedComponent: component});
      this.setState({selectedComponentParent: parent});
      // if the selected component is in the list of inputs
      // then set active to true so that it is highlighted
      let compIndex = inputs.indexOf(component)
      if (compIndex >= 0) {
        inputs[compIndex].active = true;
      }
      this.setState({inputComponents: inputs});

      // set selectedComponentStatus
      // if parent is not defined (i.e. I clicked a component in the input list
      // or component list, or I clicked the first component in the breadcrumb)
      if ( parent == undefined || parent == "" ) {
        // and component is in the recipe, then set state to selected
        if ( component.inRecipe == true ) {
          this.setState({selectedComponentStatus: "selected"});
        // and component is not in the recipe, then set state to available
        } else {
          this.setState({selectedComponentStatus: "available"});
        }
      } else { // if parent is defined (i.e. I clicked a component listed in the details view)
        // and state is selected, then state should be selected-child
        if (this.state.selectedComponentStatus == "selected") {
          this.setState({selectedComponentStatus: "selected-child"});
        // and state is available, then state should be available-child
        } else if (this.state.selectedComponentStatus == "available") {
          this.setState({selectedComponentStatus: "available-child"});
        }
        // if parent is defined
        // and state is selected-child or available-child, then state should be unchanged
      }

    } else {
      // if the user clicked on the current selected component:
      this.hideComponentDetails();
    }
  };

  hideComponentDetails() {
    this.setState({selectedComponent: ""});
    this.setState({selectedComponentStatus: ""});
    this.setState({selectedComponentParent: ""});
  }

  removeInputActive() {
    // remove the active state from list of inputs
    let inputs = this.state.inputComponents.slice(0);
    let index = inputs.indexOf(this.state.selectedComponent)
    if (index >= 0) {
      inputs[index].active = false;
    }
    return inputs;
  }

  render() {
    const recipeDisplayName = this.props.route.params.recipe;

    return (
      <Layout className="container-fluid container-pf-nav-pf-vertical">

              <button className="btn btn-primary" type="button" onClick={(e) => RecipeApi.handleSaveRecipe()}>Save</button>

              <button className="btn btn-default" type="button">Discard Changes</button>

<Link to="/recipes">Back to Recipes</Link>

<Link to={"/recipe/" + recipeDisplayName }>{recipeDisplayName}</Link>

Edit Recipe

{ recipeDisplayName }

Revision 3, Total Disk Space: 1,234 KB

          { this.state.selectedComponent == "" &&

						<Toolbar />
            { this.state.recipeComponents.length == 0 &&
            <EmptyState title={"Add Recipe Components"} message={"Browse or search for components, then add them to the recipe."} >
            </EmptyState>
            ||
            <RecipeContents components={ this.state.recipeComponents }
              dependencies={ this.state.recipeDependencies }
              handleRemoveComponent={this.handleRemoveComponent.bind(this)} handleComponentDetails={this.handleComponentDetails.bind(this)} />
            }

          ||

            <ComponentDetailsView
              parent={ recipeDisplayName }
              component={ this.state.selectedComponent }
              componentParent={ this.state.selectedComponentParent }
              status={ this.state.selectedComponentStatus }
              handleComponentDetails={this.handleComponentDetails.bind(this)}
              handleAddComponent={this.handleAddComponent.bind(this)}
              handleRemoveComponent={this.handleRemoveComponent.bind(this)} />

          }

								<form className="toolbar-pf-actions">

										Name

												<button type="button" className="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Name </button>

Type

Name

Version

Release

Lifecycle

Support Level

											<input type="text" className="form-control" id="cmpsr-recipe-input-filter" placeholder="Filter By Name..." onKeyPress={(e) => this.getFilteredInputs(e)} />

											<button className="btn btn-link btn-settings" type="button" data-toggle="modal" data-target="#cmpsr-recipe-inputs-settings">

											</button>

								</form>

                      { this.state.inputFilters.length == 0 &&
                      2,345 Available Components
                      ||
											{ this.state.filteredComponents.length } Results of 2,345 Available Components
                      }

                    { this.state.inputFilters.length > 0 &&

    											Name: { this.state.inputFilters[0].value }
    											 this.handleClearFilters(e)}>

    										 this.handleClearFilters(e)}>Clear All Filters

                    }

						  <button type="button" className="close" data-dismiss="alert" aria-hidden="true">

						  </button>

						  Select components in this list to add to the recipe.

						<ComponentInputs components={ this.state.inputFilters.length == 0 && this.state.inputComponents || this.state.filteredComponents } handleComponentDetails={this.handleComponentDetails.bind(this)} handleAddComponent={this.handleAddComponent.bind(this)} />

				<CreateComposition />

      </Layout>

    );
  }

}

export default EditRecipePage;
<commit_msg>deleting comment<commit_after>import React, { PropTypes } from 'react';
import Link from '../../components/Link';
import Layout from '../../components/Layout';
import RecipeContents from '../../components/ListView/RecipeContents';
import ComponentInputs from '../../components/ListView/ComponentInputs';
import ComponentDetailsView from '../../components/ListView/ComponentDetailsView';
import CreateComposition from '../../components/Modal/CreateComposition';
import EmptyState from '../../components/EmptyState/EmptyState';
import Toolbar from '../../components/Toolbar/Toolbar';
import constants from '../../core/constants';
import RecipeApi from '../../data/RecipeApi';
import MetadataApi from '../../data/MetadataApi';

class EditRecipePage extends React.Component {

  state = { recipe: {},
            recipeComponents: [], recipeDependencies: [],
            inputComponents: [], inputFilters: [], filteredComponents: [],
            selectedComponent: "", selectedComponentStatus: "", selectedComponentParent: "",
          };

  componentDidMount() {
    document.title = 'Welder | Recipe';
  }

  componentWillMount() {
    // get recipe, get inputs; then update inputs
      let recipeName = this.props.route.params.recipe.replace(/\s/g , "-");
      Promise.all([RecipeApi.getRecipe(recipeName), this.getInputs()]).then((data) => {
          let recipe = {
            "name": data[0].name,
            "description" : data[0].description
          };
          this.setState({recipe: recipe});
          this.setState({recipeComponents: data[0].components});
          this.setState({recipeDependencies: data[0].dependencies});
          // Recipes and available components both need to be updated before running this
          let inputs = this.updateInputs(data[1]);
          this.setState({inputComponents: inputs});

      }).catch(e => console.log('Error in EditRecipe promise: ' + e));
  }

  getInputs(filter){
    // for now, this gets full metadata and dependencies of list that's returned
    // but ideally the returned list would provide only what's needed to display
    // in the list and the popover, then the remaining data would be fetched on
    // Add or View Details
    filter = (filter === undefined) ? "" : filter;
      let p = new Promise((resolve, reject) => {
          // /modules/list looks like:
          // {"modules":[{"name":"389-ds-base","group_type":"rpm"},{"name":"389-ds-base-libs","group_type":"rpm"}, ...]}
          fetch(constants.get_modules_list + filter)
          .then(r => r.json())
          .then(data => {
              let components = data.modules
              let componentNames = MetadataApi.getNames(components);
              Promise.all([
                  MetadataApi.getData(constants.get_module_info + componentNames),
                  MetadataApi.getData(constants.get_dependencies_list + componentNames)
              ]).then((data) => {
                components = MetadataApi.updateComponentMetadata(components, data[0], true);
                components = MetadataApi.updateComponentDependencies(components, data[1]);
                components.map(i => {i.ui_type = "RPM"}) // this is being set arbitrarily for now
                resolve(components);
              }).catch(e => console.log('Error getting recipe metadata: ' + e));
          })
          .catch(e => {
              console.log("Failed to get inputs during recipe edit: " + e);
              reject();
          });
      });
      return p;
  };

  updateInputs(inputs) {
    // add a property to the original set of inputs, that indicates whether the
    // input is in the recipe or not
    let selected = this.state.recipeComponents;
    selected.map(component => {
      let index = inputs.map(input => {return input.name}).indexOf(component.name);
      if (index >= 0) {
          inputs[index].inRecipe = true;
      }
    });
    // this.setState({inputComponents: inputs});
    return inputs;
  };

  getFilteredInputs(event) {
    if (event.which == 13 || event.keyCode == 13) {
      let filter = [{
        "field": "name",
        "value": event.target.value
      }];
      Promise.all([this.getInputs("/*" + filter[0].value + "*")]).then((data) => {
        let inputs = this.updateInputs(data[0]);
        this.setState({filteredComponents : inputs});
        this.setState({inputFilters : filter});
      }).catch(e => console.log('Failed to filter inputs during recipe edit: ' + e));
      event.preventDefault();
    }
  }

  handleClearFilters() {
    this.setState({filteredComponents : []});
    this.setState({inputFilters : []});
    $('#cmpsr-recipe-input-filter').val("");
  }

  clearInputAlert() {
    $("#cmpsr-recipe-inputs .alert").remove();
  }

  handleAddComponent = (event, component, version) => {
    // the user clicked Add in the sidebar to add the component to the recipe

    // get metadata and dependencies for the component
    Promise.all([
        MetadataApi.getMetadataComponent(component, version)
    ]).then((data) => {
      let recipeComponents = this.state.recipeComponents.slice(0);
      let updatedRecipeComponents = recipeComponents.concat(data[0][0]);
      this.setState({recipeComponents: updatedRecipeComponents});
      let recipeDependencies = this.state.recipeDependencies;
      this.setState({recipeDependencies: recipeDependencies.concat(data[0][1])});
      RecipeApi.updateRecipe(data[0][0], "add");
    }).catch(e => console.log('Error getting component metadata: ' + e));

    // if an input is selected, deselect it
    let inputs = this.removeInputActive();

    // set inRecipe to true for the input component
    // in the list of available inputs
    let inputIndex = inputs.map(i => i.name).indexOf(component.name);
    if (inputIndex > -1) {
      inputs[inputIndex].inRecipe = true;
    }
    // and also the list of filtered inputs
    if (this.state.filteredComponents.length > 0) {
      let filteredComponents = this.state.filteredComponents;
      let filteredIndex = filteredComponents.map(i => i.name).indexOf(component.name);
      if (filteredIndex > -1) {
        filteredComponents[filteredIndex].inRecipe = true;
        this.setState({filteredComponents: filteredComponents});
      }
    }
    // TODO if inputs also lists dependencies, should these be indicated as included?
    this.setState({inputComponents: inputs});
    this.setState({selectedComponent: ""});
    this.setState({selectedComponentStatus: ""});
    // remove the inline message above the list of inputs
    this.clearInputAlert();
  };

  handleRemoveComponent = (event, component) => {
    // the user clicked Remove for a component in the recipe component list
    // or the component details view

    // update the recipe object (the one that's used during save)
    RecipeApi.updateRecipe(component, "remove");
    // if the removed component was visible in the details view then close the
    // details view and deselect it in the list of inputs
    let inputs = [];
    if (component == this.state.selectedComponent) {
      inputs = this.removeInputActive();
      this.hideComponentDetails();
    } else {
      inputs = this.state.inputComponents.slice(0);
    }
    // update the list of available components to include the Add button for the
    // removed component
    let inputIndex = inputs.map(i => i.name).indexOf(component.name);
    if (inputIndex > -1) {
      inputs[inputIndex].inRecipe = false;
      this.setState({inputComponents: inputs});
    }
    // update the list of recipe components to not include the removed component
    let updatedRecipeComponents = this.state.recipeComponents.slice(0);
    updatedRecipeComponents = updatedRecipeComponents.filter(obj => (obj !== component));
    this.setState({recipeComponents: updatedRecipeComponents});
  };

  handleComponentDetails = (event, component, parent) => {
    // the user selected a component in the sidebar to view more details on the right
    // remove the active state from the current selected component
    let inputs = this.removeInputActive();
    if (component != this.state.selectedComponent) {
      // if the user did not clicked on the current selected component:
      // set state for selected component
      this.setState({selectedComponent: component});
      this.setState({selectedComponentParent: parent});
      // if the selected component is in the list of inputs
      // then set active to true so that it is highlighted
      let compIndex = inputs.indexOf(component)
      if (compIndex >= 0) {
        inputs[compIndex].active = true;
      }
      this.setState({inputComponents: inputs});

      // set selectedComponentStatus
      // if parent is not defined (i.e. I clicked a component in the input list
      // or component list, or I clicked the first component in the breadcrumb)
      if ( parent == undefined || parent == "" ) {
        // and component is in the recipe, then set state to selected
        if ( component.inRecipe == true ) {
          this.setState({selectedComponentStatus: "selected"});
        // and component is not in the recipe, then set state to available
        } else {
          this.setState({selectedComponentStatus: "available"});
        }
      } else { // if parent is defined (i.e. I clicked a component listed in the details view)
        // and state is selected, then state should be selected-child
        if (this.state.selectedComponentStatus == "selected") {
          this.setState({selectedComponentStatus: "selected-child"});
        // and state is available, then state should be available-child
        } else if (this.state.selectedComponentStatus == "available") {
          this.setState({selectedComponentStatus: "available-child"});
        }
        // if parent is defined
        // and state is selected-child or available-child, then state should be unchanged
      }

    } else {
      // if the user clicked on the current selected component:
      this.hideComponentDetails();
    }
  };

  hideComponentDetails() {
    this.setState({selectedComponent: ""});
    this.setState({selectedComponentStatus: ""});
    this.setState({selectedComponentParent: ""});
  }

  removeInputActive() {
    // remove the active state from list of inputs
    let inputs = this.state.inputComponents.slice(0);
    let index = inputs.indexOf(this.state.selectedComponent)
    if (index >= 0) {
      inputs[index].active = false;
    }
    return inputs;
  }

  render() {
    const recipeDisplayName = this.props.route.params.recipe;

    return (
      <Layout className="container-fluid container-pf-nav-pf-vertical">

              <button className="btn btn-primary" type="button" onClick={(e) => RecipeApi.handleSaveRecipe()}>Save</button>

              <button className="btn btn-default" type="button">Discard Changes</button>

<Link to="/recipes">Back to Recipes</Link>

<Link to={"/recipe/" + recipeDisplayName }>{recipeDisplayName}</Link>

Edit Recipe

{ recipeDisplayName }

Revision 3, Total Disk Space: 1,234 KB

          { this.state.selectedComponent == "" &&

						<Toolbar />
            { this.state.recipeComponents.length == 0 &&
            <EmptyState title={"Add Recipe Components"} message={"Browse or search for components, then add them to the recipe."} >
            </EmptyState>
            ||
            <RecipeContents components={ this.state.recipeComponents }
              dependencies={ this.state.recipeDependencies }
              handleRemoveComponent={this.handleRemoveComponent.bind(this)} handleComponentDetails={this.handleComponentDetails.bind(this)} />
            }

          ||

            <ComponentDetailsView
              parent={ recipeDisplayName }
              component={ this.state.selectedComponent }
              componentParent={ this.state.selectedComponentParent }
              status={ this.state.selectedComponentStatus }
              handleComponentDetails={this.handleComponentDetails.bind(this)}
              handleAddComponent={this.handleAddComponent.bind(this)}
              handleRemoveComponent={this.handleRemoveComponent.bind(this)} />

          }

								<form className="toolbar-pf-actions">

										Name

												<button type="button" className="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Name </button>

Type

Name

Version

Release

Lifecycle

Support Level

											<input type="text" className="form-control" id="cmpsr-recipe-input-filter" placeholder="Filter By Name..." onKeyPress={(e) => this.getFilteredInputs(e)} />

											<button className="btn btn-link btn-settings" type="button" data-toggle="modal" data-target="#cmpsr-recipe-inputs-settings">

											</button>

								</form>

                      { this.state.inputFilters.length == 0 &&
                      2,345 Available Components
                      ||
											{ this.state.filteredComponents.length } Results of 2,345 Available Components
                      }

                    { this.state.inputFilters.length > 0 &&

    											Name: { this.state.inputFilters[0].value }
    											 this.handleClearFilters(e)}>

    										 this.handleClearFilters(e)}>Clear All Filters

                    }

						  <button type="button" className="close" data-dismiss="alert" aria-hidden="true">

						  </button>

						  Select components in this list to add to the recipe.

						<ComponentInputs components={ this.state.inputFilters.length == 0 && this.state.inputComponents || this.state.filteredComponents } handleComponentDetails={this.handleComponentDetails.bind(this)} handleAddComponent={this.handleAddComponent.bind(this)} />

				<CreateComposition />

      </Layout>

    );
  }

}

export default EditRecipePage;
<|endoftext|>