<commit_before>import _ from "underscore";
import escapeRegexp from "escape-regexp";

const ESCAPE_CHAR = "^";

const REQUIRED_SERIALIZER_PROPERTIES = ["type", "isSerializable", "serialize", "deserialize"];

class PluggableJSON {
    constructor(serializers = [], separator = "$") {
        this._validateSerializers(serializers);

        this._serializers = serializers.reduce((result, serializer) => {
            result[serializer.type] = serializer;
            return result;
        }, {});

        this._separator = separator;
        this._nonEscapedSeparator = new RegExp(`([^${escapeRegexp(ESCAPE_CHAR)}])${escapeRegexp(this._separator)}`);
    }

    serialize(value, { toObject = false } = {}) {
        let serializedObject = this._serialize(value);
        return toObject ? serializedObject : JSON.stringify(serializedObject);
    }

    _serialize(value) {
        let serializer = this.findSerializerForValue(value);

        if (serializer) {
            // returns <separator><serializer.type><separator><serialized value>
            return this._separator + this._escapeSeparators(serializer.type) +
                   this._separator + this._escapeSeparators(serializer.serialize(value));
        }
        else if (_.isArray(value)) {
            return value.map((item) => {
                return this._serialize(item);
            });
        }
        else if (_.isObject(value)) {
            return _.mapObject(value, (propertyValue) => {
                return this._serialize(propertyValue);
            });
        }
        else if (_.isString(value)){
            return this._escapeSeparators(value);
        }
        else {
            return value;
        }
    }

    _deserialize(value) {
        if (_.isString(value)) {
            let serializedComponents = this._getComponentsFromSerializedString(value);
            if (serializedComponents.type === null) {
                return serializedComponents.value;
            }
            else {
                return this._serializers[serializedComponents.type].deserialize(serializedComponents.value);
            }
        }
        else if(_.isArray(value)) {
            return value.map((item) => {
                return this._deserialize(item);
            });
        }
        else if (_.isObject(value)) {
            return _.mapObject(value, (propertyValue) => {
                return this._deserialize(propertyValue);
            });
        }
        else {
            return value;
        }
    }

    deserialize(value) {
        return this._deserialize(_.isObject(value) ? value : JSON.parse(value));
    }

    _validateSerializers(serializers) {
        let serializerTypesSeen = new Map();
        serializers.forEach((serializer, index) => {
            if (serializerTypesSeen.has(serializer.type)) {
                throw new Error(`Multiple serializers seen for type ${serializer.type}`);
            }
            serializerTypesSeen.set(serializer.type, true);
            if (_.difference(REQUIRED_SERIALIZER_PROPERTIES, Object.keys(serializer)).length !== 0) {
                throw new Error(`serializer ${index} must have the following properties: ${REQUIRED_SERIALIZER_PROPERTIES.join(",")}`);
            }
        });
    }

    _escapeSeparators(value) {
        var regex = new RegExp(escapeRegexp(this._separator), "g");
        return value.replace(regex, `${ESCAPE_CHAR}${this._separator}`);
    }

    _unescapeSeparators(value) {
        return value.replace(new RegExp(`${escapeRegexp(ESCAPE_CHAR)}${escapeRegexp(this._separator)}`, "g"), this._separator);
    }

    findSerializerForValue(value) {
        return _.values(this._serializers).find((serializer) => {
            return serializer.isSerializable(value);
        });
    }

    _getComponentsFromSerializedString(value) {
        // If the value doesn't start with a separator,
        // it is not serialized by one of the serializers
        // so return without deserializing.
        if (value.charAt(0) !== this._separator) {
            return {
                type: null,
                value: this._unescapeSeparators(value)
            };
        }

        // Find the index of the seaprator that separates
        // the serializer type from the serialized value.
        // + 1 because the returned index of `value.search`
        // is the location of the non-escaping character
        // before the separator we found.
        let separatorIndex = value.search(this._nonEscapedSeparator) + 1;

        return {
            // Start at 1 to ignore the separator at the start of the value.
            type: this._unescapeSeparators(value.substring(1, separatorIndex)),
            // + 1 to ignore the separator itself
            value: this._unescapeSeparators(value.substring(separatorIndex + 1))
        };
    }
}

export default PluggableJSON;
<commit_msg>use _.find instead of native Array.prototype.find<commit_after>import _ from "underscore";
import escapeRegexp from "escape-regexp";

const ESCAPE_CHAR = "^";

const REQUIRED_SERIALIZER_PROPERTIES = ["type", "isSerializable", "serialize", "deserialize"];

class PluggableJSON {
    constructor(serializers = [], separator = "$") {
        this._validateSerializers(serializers);

        this._serializers = serializers.reduce((result, serializer) => {
            result[serializer.type] = serializer;
            return result;
        }, {});

        this._separator = separator;
        this._nonEscapedSeparator = new RegExp(`([^${escapeRegexp(ESCAPE_CHAR)}])${escapeRegexp(this._separator)}`);
    }

    serialize(value, { toObject = false } = {}) {
        let serializedObject = this._serialize(value);
        return toObject ? serializedObject : JSON.stringify(serializedObject);
    }

    _serialize(value) {
        let serializer = this.findSerializerForValue(value);

        if (serializer) {
            // returns <separator><serializer.type><separator><serialized value>
            return this._separator + this._escapeSeparators(serializer.type) +
                   this._separator + this._escapeSeparators(serializer.serialize(value));
        }
        else if (_.isArray(value)) {
            return value.map((item) => {
                return this._serialize(item);
            });
        }
        else if (_.isObject(value)) {
            return _.mapObject(value, (propertyValue) => {
                return this._serialize(propertyValue);
            });
        }
        else if (_.isString(value)){
            return this._escapeSeparators(value);
        }
        else {
            return value;
        }
    }

    _deserialize(value) {
        if (_.isString(value)) {
            let serializedComponents = this._getComponentsFromSerializedString(value);
            if (serializedComponents.type === null) {
                return serializedComponents.value;
            }
            else {
                return this._serializers[serializedComponents.type].deserialize(serializedComponents.value);
            }
        }
        else if(_.isArray(value)) {
            return value.map((item) => {
                return this._deserialize(item);
            });
        }
        else if (_.isObject(value)) {
            return _.mapObject(value, (propertyValue) => {
                return this._deserialize(propertyValue);
            });
        }
        else {
            return value;
        }
    }

    deserialize(value) {
        return this._deserialize(_.isObject(value) ? value : JSON.parse(value));
    }

    _validateSerializers(serializers) {
        let serializerTypesSeen = new Map();
        serializers.forEach((serializer, index) => {
            if (serializerTypesSeen.has(serializer.type)) {
                throw new Error(`Multiple serializers seen for type ${serializer.type}`);
            }
            serializerTypesSeen.set(serializer.type, true);
            if (_.difference(REQUIRED_SERIALIZER_PROPERTIES, Object.keys(serializer)).length !== 0) {
                throw new Error(`serializer ${index} must have the following properties: ${REQUIRED_SERIALIZER_PROPERTIES.join(",")}`);
            }
        });
    }

    _escapeSeparators(value) {
        var regex = new RegExp(escapeRegexp(this._separator), "g");
        return value.replace(regex, `${ESCAPE_CHAR}${this._separator}`);
    }

    _unescapeSeparators(value) {
        return value.replace(new RegExp(`${escapeRegexp(ESCAPE_CHAR)}${escapeRegexp(this._separator)}`, "g"), this._separator);
    }

    findSerializerForValue(value) {
        return _.find(_.values(this._serializers), (serializer) => {
            return serializer.isSerializable(value);
        });
    }

    _getComponentsFromSerializedString(value) {
        // If the value doesn't start with a separator,
        // it is not serialized by one of the serializers
        // so return without deserializing.
        if (value.charAt(0) !== this._separator) {
            return {
                type: null,
                value: this._unescapeSeparators(value)
            };
        }

        // Find the index of the seaprator that separates
        // the serializer type from the serialized value.
        // + 1 because the returned index of `value.search`
        // is the location of the non-escaping character
        // before the separator we found.
        let separatorIndex = value.search(this._nonEscapedSeparator) + 1;

        return {
            // Start at 1 to ignore the separator at the start of the value.
            type: this._unescapeSeparators(value.substring(1, separatorIndex)),
            // + 1 to ignore the separator itself
            value: this._unescapeSeparators(value.substring(separatorIndex + 1))
        };
    }
}

export default PluggableJSON;
<|endoftext|>


<commit_before>import fs from 'fs-extra';

// Promise polyfill for ES5 compatibility
require('es6-promise').polyfill();

function log(level, message) {
  const obj = {
    datetime: Date.now(),
    severity: level,
    message: message
  };
  console.log(JSON.stringify(obj)); //eslint-disable-line no-console
}

const changesetExists = "SELECT id FROM dbchangelog WHERE id=$1";

function migrateAndStart(databaseClient, migrationsDir, cb) {
  log('info', 'Checking for migrations to perform');
  // Check whether we need to create the migration table itself
  databaseClient.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'dbchangelog'", [], (err, res) => {
    if (res.rowCount === 1) {
      migrate(databaseClient, migrationsDir, cb);
    } else {
      log('info', 'Creating the migration table');
      databaseClient.query("CREATE TABLE dbchangelog(id text, datetime timestamp with time zone);CREATE UNIQUE index changeset on dbchangelog(id);", (err, res) => {
        if (err) {
          return log('error', 'Could not create the migration table');
        }
        log('info', 'Created the migration table');
        migrate(databaseClient, migrationsDir, cb);
      });
    }
  });
}

function migrate(databaseClient, migrationsDir, cb) {
  fs.readdir(migrationsDir, (err, items) => {
    if (err) {
      // In case of an error while fetching the migrations, error and abort
      log('error', `Could not find changesets: ${err}`);
      return
    }

    // Create promises for applying the changesets. Filter the README and dbchangelog.sql files
    const promise = items
      .filter(e => e.indexOf('README.md') === -1 && e.indexOf('dbchangelog.sql') === -1)
      .sort()
      .reduce((promise, changeset) => {
        return promise.then(() => {
          return new Promise((resolve, reject) => {
            const changesetCode = changeset.replace('.sql', '')
            log('info', `Checking whether to apply changeset ${changesetCode}`)
            databaseClient.query(changesetExists, [changesetCode], (err, res) => {
              if (err) {
                // An error occurred while checking if this changeset exists
                log('error', err)
                return reject()
              }
              if (res.rowCount === 1) {
                // This changeset is already applied
                log('info', `No need to apply changeset ${changesetCode}`);
                return resolve()
              }
              log('info', `Applying changeset ${changesetCode}`);
              // Read the file content
              const changesetContent = fs.readFileSync(`${migrationsDir}/${changeset}`, 'UTF-8')
              databaseClient.query("BEGIN", err => {
                databaseClient.query(changesetContent, err => {
                  // Apply the changeset
                  if (err) {
                    // Couldn't apply the changeset, probably an error in the migration. Reject the promise
                    log('error', `Error applying the changeset ${changesetCode}: ${err}`);
                    return reject()
                  }
                  databaseClient.query("INSERT INTO dbchangelog (id, datetime) VALUES ($1, NOW())", [changesetCode], err => {
                    // Save the value of the changeset to the database
                    if (err) {
                      return reject()
                    }
                    databaseClient.query("COMMIT", err => {
                      if (err) {
                        return reject()
                      }
                      log('info', `Finished applying changeset ${changesetCode}`)
                      return resolve()
                    })
                  })
                })
              })
            })
          })
        })
      }, Promise.resolve())

    promise
      .then(cb)
      .catch(() => {
        log('error', 'One or more migrations failed to apply')
        process.exit()
      })
  })
}

export default migrateAndStart;
<commit_msg>Avoid catching callback errors<commit_after>import fs from 'fs-extra';

// Promise polyfill for ES5 compatibility
require('es6-promise').polyfill();

function log(level, message) {
  const obj = {
    datetime: Date.now(),
    severity: level,
    message: message
  };
  console.log(JSON.stringify(obj)); //eslint-disable-line no-console
}

const changesetExists = "SELECT id FROM dbchangelog WHERE id=$1";

function migrateAndStart(databaseClient, migrationsDir, cb) {
  log('info', 'Checking for migrations to perform');
  // Check whether we need to create the migration table itself
  databaseClient.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'dbchangelog'", [], (err, res) => {
    if (res.rowCount === 1) {
      migrate(databaseClient, migrationsDir, cb);
    } else {
      log('info', 'Creating the migration table');
      databaseClient.query("CREATE TABLE dbchangelog(id text, datetime timestamp with time zone);CREATE UNIQUE index changeset on dbchangelog(id);", (err, res) => {
        if (err) {
          return log('error', 'Could not create the migration table');
        }
        log('info', 'Created the migration table');
        migrate(databaseClient, migrationsDir, cb);
      });
    }
  });
}

function migrate(databaseClient, migrationsDir, cb) {
  fs.readdir(migrationsDir, (err, items) => {
    if (err) {
      // In case of an error while fetching the migrations, error and abort
      log('error', `Could not find changesets: ${err}`);
      return
    }

    // Create promises for applying the changesets. Filter the README and dbchangelog.sql files
    const promise = items
      .filter(e => e.indexOf('README.md') === -1 && e.indexOf('dbchangelog.sql') === -1)
      .sort()
      .reduce((promise, changeset) => {
        return promise.then(() => {
          return new Promise((resolve, reject) => {
            const changesetCode = changeset.replace('.sql', '')
            log('info', `Checking whether to apply changeset ${changesetCode}`)
            databaseClient.query(changesetExists, [changesetCode], (err, res) => {
              if (err) {
                // An error occurred while checking if this changeset exists
                log('error', err)
                return reject()
              }
              if (res.rowCount === 1) {
                // This changeset is already applied
                log('info', `No need to apply changeset ${changesetCode}`);
                return resolve()
              }
              log('info', `Applying changeset ${changesetCode}`);
              // Read the file content
              const changesetContent = fs.readFileSync(`${migrationsDir}/${changeset}`, 'UTF-8')
              databaseClient.query("BEGIN", err => {
                databaseClient.query(changesetContent, err => {
                  // Apply the changeset
                  if (err) {
                    // Couldn't apply the changeset, probably an error in the migration. Reject the promise
                    log('error', `Error applying the changeset ${changesetCode}: ${err}`);
                    return reject()
                  }
                  databaseClient.query("INSERT INTO dbchangelog (id, datetime) VALUES ($1, NOW())", [changesetCode], err => {
                    // Save the value of the changeset to the database
                    if (err) {
                      return reject()
                    }
                    databaseClient.query("COMMIT", err => {
                      if (err) {
                        return reject()
                      }
                      log('info', `Finished applying changeset ${changesetCode}`)
                      return resolve()
                    })
                  })
                })
              })
            })
          })
        })
      }, Promise.resolve())

    promise
      .catch(err => {
        console.log(err)
        log('error', 'One or more migrations failed to apply')
        process.exit()
      })
      .then(cb)
  })
}

export default migrateAndStart;
<|endoftext|>


<commit_before>/*
 * Swipe 2.0
 *
 * Brad Birdsall and Xiuyu Li
 * Copyright 2015, MIT License
 *
*/

function Swipe(container, options) {

  "use strict";

  // utilities
  var noop = function() {}; // simple no operation function
  var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution

  // check browser capabilities
  var browser = {
    addEventListener: !!window.addEventListener,
    touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
    transitions: (function(temp) {
      var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
      for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true;
      return false;
    })(document.createElement('swipe'))
  };

  // quit if no root element
  if (!container) return;
  var element = container.children[0];
  var slides, slidePos, width, length;
  options = options || {};
  var index = parseInt(options.startSlide, 10) || 0;
  var speed = options.speed || 300;
  options.continuous = options.continuous !== undefined ? options.continuous : true;

  // AutoRestart option: auto restart slideshow after user's touch event
  options.autoRestart = options.autoRestart !== undefined ? options.autoRestart : true;

  function setup() {

    // cache slides
    slides = element.children;
    length = slides.length;

    // set continuous to false if only one slide
    if (slides.length < 2) options.continuous = false;

    //special case if two slides
    if (browser.transitions && options.continuous && slides.length < 3) {
      element.appendChild(slides[0].cloneNode(true));
      element.appendChild(element.children[1].cloneNode(true));
      slides = element.children;
    }

    // create an array to store current positions of each slide
    slidePos = new Array(slides.length);

    // determine width of each slide
    width = container.getBoundingClientRect().width || container.offsetWidth;

    element.style.width = (slides.length * width) + 'px';

    // stack elements
    var pos = slides.length;
    while (pos--) {

      var slide = slides[pos];

      slide.style.width = width + 'px';
      slide.setAttribute('data-index', pos);

      if (browser.transitions) {
        slide.style.left = (pos * -width) + 'px';
        move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
      }

    }

    // reposition elements before and after index
    if (options.continuous && browser.transitions) {
      move(circle(index-1), -width, 0);
      move(circle(index+1), width, 0);
    }

    if (!browser.transitions) element.style.left = (index * -width) + 'px';

    container.style.visibility = 'visible';

  }

  function prev() {

    if (options.continuous) slide(index-1);
    else if (index) slide(index-1);

  }

  function next() {

    if (options.continuous) slide(index+1);
    else if (index < slides.length - 1) slide(index+1);

  }

  function circle(index) {

    // a simple positive modulo using slides.length
    return (slides.length + (index % slides.length)) % slides.length;

  }

  function slide(to, slideSpeed) {

    // do nothing if already on requested slide
    if (index == to) return;

    if (browser.transitions) {

      var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward

      // get the actual position of the slide
      if (options.continuous) {
        var natural_direction = direction;
        direction = -slidePos[circle(to)] / width;

        // if going forward but to < index, use to = slides.length + to
        // if going backward but to > index, use to = -slides.length + to
        if (direction !== natural_direction) to =  -direction * slides.length + to;

      }

      var diff = Math.abs(index-to) - 1;

      // move all the slides between index and to in the right direction
      while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0);

      to = circle(to);

      move(index, width * direction, slideSpeed || speed);
      move(to, 0, slideSpeed || speed);

      if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place

    } else {

      to = circle(to);
      animate(index * -width, to * -width, slideSpeed || speed);
      //no fallback for a circular continuous if the browser does not accept transitions
    }

    index = to;
    offloadFn(options.callback && options.callback(index, slides[index]));
  }

  function move(index, dist, speed) {

    translate(index, dist, speed);
    slidePos[index] = dist;

  }

  function translate(index, dist, speed) {

    var slide = slides[index];
    var style = slide && slide.style;

    if (!style) return;

    style.webkitTransitionDuration =
    style.MozTransitionDuration =
    style.msTransitionDuration =
    style.OTransitionDuration =
    style.transitionDuration = speed + 'ms';

    style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
    style.msTransform =
    style.MozTransform =
    style.OTransform = 'translateX(' + dist + 'px)';

  }

  function animate(from, to, speed) {

    // if not an animation, just reposition
    if (!speed) {

      element.style.left = to + 'px';
      return;

    }

    var start = +new Date;

    var timer = setInterval(function() {

      var timeElap = +new Date - start;

      if (timeElap > speed) {

        element.style.left = to + 'px';

        if (delay) begin();

        options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

        clearInterval(timer);
        return;

      }

      element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';

    }, 4);

  }

  // setup auto slideshow
  var delay = options.auto || 0;
  var interval;

  function begin() {

    interval = setTimeout(next, delay);

  }

  function stop() {

    delay = 0;
    clearTimeout(interval);

  }

  function restart() {
    stop();
    delay = options.auto || 0;
    begin();
  }

  // setup initial vars
  var start = {};
  var delta = {};
  var isScrolling;

  // setup event capturing
  var events = {

    handleEvent: function(event) {

      switch (event.type) {
        case 'touchstart': this.start(event); break;
        case 'touchmove': this.move(event); break;
        case 'touchend': offloadFn(this.end(event)); break;
        case 'webkitTransitionEnd':
        case 'msTransitionEnd':
        case 'oTransitionEnd':
        case 'otransitionend':
        case 'transitionend': offloadFn(this.transitionEnd(event)); break;
        case 'resize': offloadFn(setup); break;
      }

      if (options.stopPropagation) event.stopPropagation();

    },
    start: function(event) {

      var touches = event.touches[0];

      // measure start values
      start = {

        // get initial touch coords
        x: touches.pageX,
        y: touches.pageY,

        // store time to determine touch duration
        time: +new Date

      };

      // used for testing first move event
      isScrolling = undefined;

      // reset delta and end measurements
      delta = {};

      // attach touchmove and touchend listeners
      element.addEventListener('touchmove', this, false);
      element.addEventListener('touchend', this, false);

    },
    move: function(event) {

      // ensure swiping with one touch and not pinching
      if ( event.touches.length > 1 || event.scale && event.scale !== 1) return

      if (options.disableScroll) event.preventDefault();

      var touches = event.touches[0];

      // measure change in x and y
      delta = {
        x: touches.pageX - start.x,
        y: touches.pageY - start.y
      };

      // determine if scrolling test has run - one time test
      if ( typeof isScrolling === 'undefined') {
        isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );
      }

      // if user is not trying to scroll vertically
      if (!isScrolling) {

        // prevent native scrolling
        event.preventDefault();

        // stop slideshow
        stop();

        // increase resistance if first or last slide
        if (options.continuous) { // we don't add resistance at the end

          translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0);
          translate(index, delta.x + slidePos[index], 0);
          translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0);

        } else {

          delta.x =
            delta.x /
              ( (!index && delta.x > 0               // if first slide and sliding left
                || index == slides.length - 1        // or if last slide and sliding right
                && delta.x < 0                       // and if sliding at all
              ) ?
              ( Math.abs(delta.x) / width + 1 )      // determine resistance level
              : 1 );                                 // no resistance if false

          // translate 1:1
          translate(index-1, delta.x + slidePos[index-1], 0);
          translate(index, delta.x + slidePos[index], 0);
          translate(index+1, delta.x + slidePos[index+1], 0);
        }

      }

    },
    end: function(event) {

      // measure duration
      var duration = +new Date - start.time;

      // determine if slide attempt triggers next/prev slide
      var isValidSlide =
            Number(duration) < 250               // if slide duration is less than 250ms
            && Math.abs(delta.x) > 20            // and if slide amt is greater than 20px
            || Math.abs(delta.x) > width/2;      // or if slide amt is greater than half the width

      // determine if slide attempt is past start and end
      var isPastBounds =
            !index && delta.x > 0                            // if first slide and slide amt is greater than 0
            || index == slides.length - 1 && delta.x < 0;    // or if last slide and slide amt is less than 0

      if (options.continuous) isPastBounds = false;

      // determine direction of swipe (true:right, false:left)
      var direction = delta.x < 0;

      // if not scrolling vertically
      if (!isScrolling) {

        if (isValidSlide && !isPastBounds) {

          if (direction) {

            if (options.continuous) { // we need to get the next in this direction in place

              move(circle(index-1), -width, 0);
              move(circle(index+2), width, 0);

            } else {
              move(index-1, -width, 0);
            }

            move(index, slidePos[index]-width, speed);
            move(circle(index+1), slidePos[circle(index+1)]-width, speed);
            index = circle(index+1);

          } else {
            if (options.continuous) { // we need to get the next in this direction in place

              move(circle(index+1), width, 0);
              move(circle(index-2), -width, 0);

            } else {
              move(index+1, width, 0);
            }

            move(index, slidePos[index]+width, speed);
            move(circle(index-1), slidePos[circle(index-1)]+width, speed);
            index = circle(index-1);

          }

          options.callback && options.callback(index, slides[index]);

        } else {

          if (options.continuous) {

            move(circle(index-1), -width, speed);
            move(index, 0, speed);
            move(circle(index+1), width, speed);

          } else {

            move(index-1, -width, speed);
            move(index, 0, speed);
            move(index+1, width, speed);
          }

        }

      }

      // kill touchmove and touchend event listeners until touchstart called again
      element.removeEventListener('touchmove', events, false)
      element.removeEventListener('touchend', events, false)

    },
    transitionEnd: function(event) {

      if (parseInt(event.target.getAttribute('data-index'), 10) == index) {

        if (delay || options.autoRestart) restart();

        options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

      }

    }

  };

  // trigger setup
  setup();

  // start auto slideshow if applicable
  if (delay) begin();

  // add event listeners
  if (browser.addEventListener) {

    // set touchstart event on element
    if (browser.touch) element.addEventListener('touchstart', events, false);

    if (browser.transitions) {
      element.addEventListener('webkitTransitionEnd', events, false);
      element.addEventListener('msTransitionEnd', events, false);
      element.addEventListener('oTransitionEnd', events, false);
      element.addEventListener('otransitionend', events, false);
      element.addEventListener('transitionend', events, false);
    }

    // set resize event on window
    window.addEventListener('resize', events, false);

  } else {

    window.onresize = function () { setup() }; // to play nice with old IE

  }

  // expose the Swipe API
  return {
    setup: function() {

      setup();

    },
    slide: function(to, speed) {

      // cancel slideshow
      stop();

      slide(to, speed);

    },
    prev: function() {

      // cancel slideshow
      stop();

      prev();

    },
    next: function() {

      // cancel slideshow
      stop();

      next();

    },
    restart: function() {

      // Restart slideshow
      restart();

    },
    stop: function() {

      // cancel slideshow
      stop();

    },
    getPos: function() {

      // return current index position
      return index;

    },
    getNumSlides: function() {

      // return total number of slides
      return length;
    },
    kill: function() {

      // cancel slideshow
      stop();

      // reset element
      element.style.width = '';
      element.style.left = '';

      // reset slides
      var pos = slides.length;
      while (pos--) {

        var slide = slides[pos];
        slide.style.width = '';
        slide.style.left = '';

        if (browser.transitions) translate(pos, 0, 0);

      }

      // removed event listeners
      if (browser.addEventListener) {

        // remove current event listeners
        element.removeEventListener('touchstart', events, false);
        element.removeEventListener('webkitTransitionEnd', events, false);
        element.removeEventListener('msTransitionEnd', events, false);
        element.removeEventListener('oTransitionEnd', events, false);
        element.removeEventListener('otransitionend', events, false);
        element.removeEventListener('transitionend', events, false);
        window.removeEventListener('resize', events, false);

      }
      else {

        window.onresize = null;

      }

    }
  }

}

(function (factory) {
  if(typeof module === "object" && typeof module.exports === "object") {
    factory(require("jquery"), window, document);
  } else {
    factory(jQuery, window, document);
  }
}(function($, window, document, undefined) {
  if ( window.jQuery || window.Zepto ) {
    (function($) {
      $.fn.Swipe = function(params) {
        return this.each(function() {
          $(this).data('Swipe', new Swipe($(this)[0], params));
        });
      }
    })( window.jQuery || window.Zepto )
  }
}));
<commit_msg>fix module.exports<commit_after>/*
 * Swipe 2.0
 *
 * Brad Birdsall and Xiuyu Li
 * Copyright 2015, MIT License
 *
*/

function Swipe(container, options) {

  'use strict';

  // utilities
  var noop = function() {}; // simple no operation function
  var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution

  // check browser capabilities
  var browser = {
    addEventListener: !!window.addEventListener,
    touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
    transitions: (function(temp) {
      var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
      for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true;
      return false;
    })(document.createElement('swipe'))
  };

  // quit if no root element
  if (!container) return;
  var element = container.children[0];
  var slides, slidePos, width, length;
  options = options || {};
  var index = parseInt(options.startSlide, 10) || 0;
  var speed = options.speed || 300;
  options.continuous = options.continuous !== undefined ? options.continuous : true;

  // AutoRestart option: auto restart slideshow after user's touch event
  options.autoRestart = options.autoRestart !== undefined ? options.autoRestart : true;

  function setup() {

    // cache slides
    slides = element.children;
    length = slides.length;

    // set continuous to false if only one slide
    if (slides.length < 2) options.continuous = false;

    //special case if two slides
    if (browser.transitions && options.continuous && slides.length < 3) {
      element.appendChild(slides[0].cloneNode(true));
      element.appendChild(element.children[1].cloneNode(true));
      slides = element.children;
    }

    // create an array to store current positions of each slide
    slidePos = new Array(slides.length);

    // determine width of each slide
    width = container.getBoundingClientRect().width || container.offsetWidth;

    element.style.width = (slides.length * width) + 'px';

    // stack elements
    var pos = slides.length;
    while (pos--) {

      var slide = slides[pos];

      slide.style.width = width + 'px';
      slide.setAttribute('data-index', pos);

      if (browser.transitions) {
        slide.style.left = (pos * -width) + 'px';
        move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
      }

    }

    // reposition elements before and after index
    if (options.continuous && browser.transitions) {
      move(circle(index-1), -width, 0);
      move(circle(index+1), width, 0);
    }

    if (!browser.transitions) element.style.left = (index * -width) + 'px';

    container.style.visibility = 'visible';

  }

  function prev() {

    if (options.continuous) slide(index-1);
    else if (index) slide(index-1);

  }

  function next() {

    if (options.continuous) slide(index+1);
    else if (index < slides.length - 1) slide(index+1);

  }

  function circle(index) {

    // a simple positive modulo using slides.length
    return (slides.length + (index % slides.length)) % slides.length;

  }

  function slide(to, slideSpeed) {

    // do nothing if already on requested slide
    if (index == to) return;

    if (browser.transitions) {

      var direction = Math.abs(index-to) / (index-to); // 1: backward, -1: forward

      // get the actual position of the slide
      if (options.continuous) {
        var natural_direction = direction;
        direction = -slidePos[circle(to)] / width;

        // if going forward but to < index, use to = slides.length + to
        // if going backward but to > index, use to = -slides.length + to
        if (direction !== natural_direction) to =  -direction * slides.length + to;

      }

      var diff = Math.abs(index-to) - 1;

      // move all the slides between index and to in the right direction
      while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0);

      to = circle(to);

      move(index, width * direction, slideSpeed || speed);
      move(to, 0, slideSpeed || speed);

      if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place

    } else {

      to = circle(to);
      animate(index * -width, to * -width, slideSpeed || speed);
      //no fallback for a circular continuous if the browser does not accept transitions
    }

    index = to;
    offloadFn(options.callback && options.callback(index, slides[index]));
  }

  function move(index, dist, speed) {

    translate(index, dist, speed);
    slidePos[index] = dist;

  }

  function translate(index, dist, speed) {

    var slide = slides[index];
    var style = slide && slide.style;

    if (!style) return;

    style.webkitTransitionDuration =
    style.MozTransitionDuration =
    style.msTransitionDuration =
    style.OTransitionDuration =
    style.transitionDuration = speed + 'ms';

    style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
    style.msTransform =
    style.MozTransform =
    style.OTransform = 'translateX(' + dist + 'px)';

  }

  function animate(from, to, speed) {

    // if not an animation, just reposition
    if (!speed) {

      element.style.left = to + 'px';
      return;

    }

    var start = +new Date;

    var timer = setInterval(function() {

      var timeElap = +new Date - start;

      if (timeElap > speed) {

        element.style.left = to + 'px';

        if (delay) begin();

        options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

        clearInterval(timer);
        return;

      }

      element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';

    }, 4);

  }

  // setup auto slideshow
  var delay = options.auto || 0;
  var interval;

  function begin() {

    interval = setTimeout(next, delay);

  }

  function stop() {

    delay = 0;
    clearTimeout(interval);

  }

  function restart() {
    stop();
    delay = options.auto || 0;
    begin();
  }

  // setup initial vars
  var start = {};
  var delta = {};
  var isScrolling;

  // setup event capturing
  var events = {

    handleEvent: function(event) {

      switch (event.type) {
        case 'touchstart': this.start(event); break;
        case 'touchmove': this.move(event); break;
        case 'touchend': offloadFn(this.end(event)); break;
        case 'webkitTransitionEnd':
        case 'msTransitionEnd':
        case 'oTransitionEnd':
        case 'otransitionend':
        case 'transitionend': offloadFn(this.transitionEnd(event)); break;
        case 'resize': offloadFn(setup); break;
      }

      if (options.stopPropagation) event.stopPropagation();

    },
    start: function(event) {

      var touches = event.touches[0];

      // measure start values
      start = {

        // get initial touch coords
        x: touches.pageX,
        y: touches.pageY,

        // store time to determine touch duration
        time: +new Date

      };

      // used for testing first move event
      isScrolling = undefined;

      // reset delta and end measurements
      delta = {};

      // attach touchmove and touchend listeners
      element.addEventListener('touchmove', this, false);
      element.addEventListener('touchend', this, false);

    },
    move: function(event) {

      // ensure swiping with one touch and not pinching
      if ( event.touches.length > 1 || event.scale && event.scale !== 1) return

      if (options.disableScroll) event.preventDefault();

      var touches = event.touches[0];

      // measure change in x and y
      delta = {
        x: touches.pageX - start.x,
        y: touches.pageY - start.y
      };

      // determine if scrolling test has run - one time test
      if ( typeof isScrolling === 'undefined') {
        isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );
      }

      // if user is not trying to scroll vertically
      if (!isScrolling) {

        // prevent native scrolling
        event.preventDefault();

        // stop slideshow
        stop();

        // increase resistance if first or last slide
        if (options.continuous) { // we don't add resistance at the end

          translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0);
          translate(index, delta.x + slidePos[index], 0);
          translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0);

        } else {

          delta.x =
            delta.x /
              ( (!index && delta.x > 0               // if first slide and sliding left
                || index == slides.length - 1        // or if last slide and sliding right
                && delta.x < 0                       // and if sliding at all
              ) ?
              ( Math.abs(delta.x) / width + 1 )      // determine resistance level
              : 1 );                                 // no resistance if false

          // translate 1:1
          translate(index-1, delta.x + slidePos[index-1], 0);
          translate(index, delta.x + slidePos[index], 0);
          translate(index+1, delta.x + slidePos[index+1], 0);
        }

      }

    },
    end: function(event) {

      // measure duration
      var duration = +new Date - start.time;

      // determine if slide attempt triggers next/prev slide
      var isValidSlide =
            Number(duration) < 250               // if slide duration is less than 250ms
            && Math.abs(delta.x) > 20            // and if slide amt is greater than 20px
            || Math.abs(delta.x) > width/2;      // or if slide amt is greater than half the width

      // determine if slide attempt is past start and end
      var isPastBounds =
            !index && delta.x > 0                            // if first slide and slide amt is greater than 0
            || index == slides.length - 1 && delta.x < 0;    // or if last slide and slide amt is less than 0

      if (options.continuous) isPastBounds = false;

      // determine direction of swipe (true:right, false:left)
      var direction = delta.x < 0;

      // if not scrolling vertically
      if (!isScrolling) {

        if (isValidSlide && !isPastBounds) {

          if (direction) {

            if (options.continuous) { // we need to get the next in this direction in place

              move(circle(index-1), -width, 0);
              move(circle(index+2), width, 0);

            } else {
              move(index-1, -width, 0);
            }

            move(index, slidePos[index]-width, speed);
            move(circle(index+1), slidePos[circle(index+1)]-width, speed);
            index = circle(index+1);

          } else {
            if (options.continuous) { // we need to get the next in this direction in place

              move(circle(index+1), width, 0);
              move(circle(index-2), -width, 0);

            } else {
              move(index+1, width, 0);
            }

            move(index, slidePos[index]+width, speed);
            move(circle(index-1), slidePos[circle(index-1)]+width, speed);
            index = circle(index-1);

          }

          options.callback && options.callback(index, slides[index]);

        } else {

          if (options.continuous) {

            move(circle(index-1), -width, speed);
            move(index, 0, speed);
            move(circle(index+1), width, speed);

          } else {

            move(index-1, -width, speed);
            move(index, 0, speed);
            move(index+1, width, speed);
          }

        }

      }

      // kill touchmove and touchend event listeners until touchstart called again
      element.removeEventListener('touchmove', events, false)
      element.removeEventListener('touchend', events, false)

    },
    transitionEnd: function(event) {

      if (parseInt(event.target.getAttribute('data-index'), 10) == index) {

        if (delay || options.autoRestart) restart();

        options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

      }

    }

  };

  // trigger setup
  setup();

  // start auto slideshow if applicable
  if (delay) begin();

  // add event listeners
  if (browser.addEventListener) {

    // set touchstart event on element
    if (browser.touch) element.addEventListener('touchstart', events, false);

    if (browser.transitions) {
      element.addEventListener('webkitTransitionEnd', events, false);
      element.addEventListener('msTransitionEnd', events, false);
      element.addEventListener('oTransitionEnd', events, false);
      element.addEventListener('otransitionend', events, false);
      element.addEventListener('transitionend', events, false);
    }

    // set resize event on window
    window.addEventListener('resize', events, false);

  } else {

    window.onresize = function () { setup() }; // to play nice with old IE

  }

  // expose the Swipe API
  return {
    setup: function() {

      setup();

    },
    slide: function(to, speed) {

      // cancel slideshow
      stop();

      slide(to, speed);

    },
    prev: function() {

      // cancel slideshow
      stop();

      prev();

    },
    next: function() {

      // cancel slideshow
      stop();

      next();

    },
    restart: function() {

      // Restart slideshow
      restart();

    },
    stop: function() {

      // cancel slideshow
      stop();

    },
    getPos: function() {

      // return current index position
      return index;

    },
    getNumSlides: function() {

      // return total number of slides
      return length;
    },
    kill: function() {

      // cancel slideshow
      stop();

      // reset element
      element.style.width = '';
      element.style.left = '';

      // reset slides
      var pos = slides.length;
      while (pos--) {

        var slide = slides[pos];
        slide.style.width = '';
        slide.style.left = '';

        if (browser.transitions) translate(pos, 0, 0);

      }

      // removed event listeners
      if (browser.addEventListener) {

        // remove current event listeners
        element.removeEventListener('touchstart', events, false);
        element.removeEventListener('webkitTransitionEnd', events, false);
        element.removeEventListener('msTransitionEnd', events, false);
        element.removeEventListener('oTransitionEnd', events, false);
        element.removeEventListener('otransitionend', events, false);
        element.removeEventListener('transitionend', events, false);
        window.removeEventListener('resize', events, false);

      }
      else {

        window.onresize = null;

      }

    }
  }

}

if(typeof module === 'object' && typeof module.exports === 'object') {
  module.exports = Swipe;
}

;(function($) {
  if (!$) return
  $.fn.Swipe = function(params) {
    return this.each(function() {
      $(this).data('Swipe', new Swipe($(this)[0], params));
    });
  }
})( window.jQuery || window.Zepto );
<|endoftext|>


<commit_before>'use strict';

const electron = require('electron');
const remote = electron.remote;
const ipc = electron.ipcRenderer;
const $ = require('jQuery');

let templateList = $('#template-list');
templateList.find('#new').click(handleNewTemplateClick);

let templateEdit = $('#template-edit');
templateEdit.find('#back').click(handleTemplateBackClick);
templateEdit.find('#new').click(handleNewPropertyClick);
templateEdit.find('#delete').click(handleTemplateDeleteClick);
templateEdit.find('#delete').mouseleave(handleDeleteOut);
templateEdit.find('#delete').find('#confirm').hide();
templateEdit.find('#name').change(function(e) {
    ipc.send('update-template', currentTemplateUUID, { name:$(this).val() });
});

let propertyEdit = $('#property-edit');
propertyEdit.find('#delete').click(handlePropertyDeleteClick);
propertyEdit.find('#delete').mouseleave(handleDeleteOut);
propertyEdit.find('#delete').find('#confirm').hide();
propertyEdit.find('#back').click(handlePropertyBackClick);
propertyEdit.find("#name").change(function(e) {
    ipc.send('update-property', currentTemplateUUID, currentPropertyUUID, { name:$(this).val() });
});
propertyEdit.find("#type").change(function(e) {
    ipc.send('update-property', currentTemplateUUID, currentPropertyUUID, { type:$(this).val() });
});
propertyEdit.find("#defaultValue").change(function(e) {
    ipc.send('update-property', currentTemplateUUID, currentPropertyUUID, { defaultValue:$(this).val() });
});

let currentTemplateUUID = null;
let currentPropertyUUID = null;

// values: list, template, property
let state = 'list';

// init
templateEdit.hide();
propertyEdit.hide();

let templates = ipc.sendSync('request-templates');
for (let t in templates) {
    if (templates.hasOwnProperty(t)) {
        let template = templates[t];
        addTemplate(template);
    }
}

function handleNewTemplateClick() {
    ipc.send('create-template');
}

ipc.on('template-created', function(event, data) {
    addTemplate(data);
});

function addTemplate(data) {
    let template = templateList.find('#template').find('div').first().clone();
    templateList.find('#list').append(template);
    template.attr('uuid', data.uuid);
    template.find('#name').text(data.name);
    template.find('#edit').click(handleEditTemplateClick);
}

function handleEditTemplateClick(e) {
    state = 'template';
    currentTemplateUUID = $(this).closest('.panel').attr('uuid');
    let template = ipc.sendSync('request-template', currentTemplateUUID)

    templateEdit.find('#name').val(template.name);

    templateEdit.find('#list').empty();
    for (let property in template.properties) {
        if (template.properties.hasOwnProperty(property)) {
            addProperty(template.properties[property]);
        }
    }

    templateList.hide();
    templateEdit.show();
}

ipc.on('template-updated', function(event, template) {
    let panel = templateList.find('.panel[uuid="' + template.uuid + '"]');
    panel.find('#name').text(template.name);
});

function handleDeleteOut() {
    $(this).find("#confirm").hide();
    $(this).find("#normal").show();
}

function handleTemplateDeleteClick() {
    let confirmIcon = $(this).find("#confirm");
    let normalIcon = $(this).find("#normal");
    let firstClick = normalIcon.is(":visible");
    if (firstClick) {
        normalIcon.hide();
        confirmIcon.show();
    } else {
        normalIcon.show();
        confirmIcon.hide();
        ipc.send('delete-template', currentTemplateUUID);
        handleTemplateBackClick();
    }
}

ipc.on('template-deleted', function(event, uuid) {
    let panel = templateList.find('.panel[uuid="' + uuid + '"]');
    panel.remove();
});

function handlePropertyDeleteClick() {
    let confirmIcon = $(this).find("#confirm");
    let normalIcon = $(this).find("#normal");
    let firstClick = normalIcon.is(":visible");
    if (firstClick) {
        normalIcon.hide();
        confirmIcon.show();
    } else {
        normalIcon.show();
        confirmIcon.hide();
        ipc.send('delete-property', currentTemplateUUID, currentPropertyUUID);
        handlePropertyBackClick();
    }
}

ipc.on('property-deleted', function(event, templateUUID, propertyUUID) {
    let panel = templateEdit.find('.panel[uuid="' + propertyUUID + '"]');
    panel.remove();
});

function handleTemplateBackClick() {
    state = 'list';
    currentTemplateUUID = null;
    templateList.show();
    templateEdit.hide();
}

function handleNewPropertyClick() {
    ipc.send('create-property', currentTemplateUUID);
}

ipc.on('property-created', function(event, template, property) {
    if (state == 'template' && template.uuid == currentTemplateUUID) {
        addProperty(property);
    }
});

function addProperty(data) {
    let property = templateEdit.find('#template').find('div').first().clone();
    templateEdit.find('#list').append(property);
    property.attr('uuid', data.uuid);
    property.find('#name').text(data.name);
    property.find('#type').text(data.type);
    property.find('#edit').click(handleEditProperty);
}

function handleEditProperty() {
    state = 'property';
    currentPropertyUUID = $(this).closest('.panel').attr('uuid');

    updateProperty();

    templateEdit.hide();
    propertyEdit.show();
}

function updateProperty() {
    let text = propertyEdit.find("#text");
    let checkbox = propertyEdit.find("#checkbox");
    let template = ipc.sendSync('request-template', currentTemplateUUID);
    let property = template.properties[currentPropertyUUID];

    propertyEdit.find("#name").val(property.name);
    propertyEdit.find("#type").val(property.type);

    if (property.type == 'boolean') {
        text.hide();
        checkbox.show();
        checkbox.find('#defaultValue').attr('checked', property.defaultValue);
    } else {
        text.show();
        checkbox.hide();
        text.find("#defaultValue").val(property.defaultValue);
    }
}

ipc.on('property-updated', function(event, template, property) {
    // update property in template edit
    let panel = templateEdit.find('.panel[uuid="' + property.uuid + '"]');
    panel.find('#name').text(property.name);
    panel.find('#type').text(property.type);
    // update property in property edit
    if (state == 'property' && property.uuid == currentPropertyUUID) {
        updateProperty();
    }
});

function handlePropertyBackClick() {
    state = 'template';
    currentPropertyUUID = null;
    templateEdit.show();
    propertyEdit.hide();
}
<commit_msg>moved init code into its own function<commit_after>'use strict';

const electron = require('electron');
const remote = electron.remote;
const ipc = electron.ipcRenderer;
const $ = require('jQuery');

let templateList = $('#template-list');
templateList.find('#new').click(handleNewTemplateClick);

let templateEdit = $('#template-edit');
templateEdit.find('#back').click(handleTemplateBackClick);
templateEdit.find('#new').click(handleNewPropertyClick);
templateEdit.find('#delete').click(handleTemplateDeleteClick);
templateEdit.find('#delete').mouseleave(handleDeleteOut);
templateEdit.find('#delete').find('#confirm').hide();
templateEdit.find('#name').change(function(e) {
    ipc.send('update-template', currentTemplateUUID, { name:$(this).val() });
});

let propertyEdit = $('#property-edit');
propertyEdit.find('#delete').click(handlePropertyDeleteClick);
propertyEdit.find('#delete').mouseleave(handleDeleteOut);
propertyEdit.find('#delete').find('#confirm').hide();
propertyEdit.find('#back').click(handlePropertyBackClick);
propertyEdit.find("#name").change(function(e) {
    ipc.send('update-property', currentTemplateUUID, currentPropertyUUID, { name:$(this).val() });
});
propertyEdit.find("#type").change(function(e) {
    ipc.send('update-property', currentTemplateUUID, currentPropertyUUID, { type:$(this).val() });
});
propertyEdit.find("#defaultValue").change(function(e) {
    ipc.send('update-property', currentTemplateUUID, currentPropertyUUID, { defaultValue:$(this).val() });
});

let currentTemplateUUID = null;
let currentPropertyUUID = null;

// values: list, template, property
let state = 'list';

templateEdit.hide();
propertyEdit.hide();

init();

function init() {
    let templates = ipc.sendSync('request-templates');
    for (let t in templates) {
        if (templates.hasOwnProperty(t)) {
            let template = templates[t];
            addTemplate(template);
        }
    }
}

function handleNewTemplateClick() {
    ipc.send('create-template');
}

ipc.on('template-created', function(event, data) {
    addTemplate(data);
});

function addTemplate(data) {
    let template = templateList.find('#template').find('div').first().clone();
    templateList.find('#list').append(template);
    template.attr('uuid', data.uuid);
    template.find('#name').text(data.name);
    template.find('#edit').click(handleEditTemplateClick);
}

function handleEditTemplateClick(e) {
    state = 'template';
    currentTemplateUUID = $(this).closest('.panel').attr('uuid');
    let template = ipc.sendSync('request-template', currentTemplateUUID)

    templateEdit.find('#name').val(template.name);

    templateEdit.find('#list').empty();
    for (let property in template.properties) {
        if (template.properties.hasOwnProperty(property)) {
            addProperty(template.properties[property]);
        }
    }

    templateList.hide();
    templateEdit.show();
}

ipc.on('template-updated', function(event, template) {
    let panel = templateList.find('.panel[uuid="' + template.uuid + '"]');
    panel.find('#name').text(template.name);
});

function handleDeleteOut() {
    $(this).find("#confirm").hide();
    $(this).find("#normal").show();
}

function handleTemplateDeleteClick() {
    let confirmIcon = $(this).find("#confirm");
    let normalIcon = $(this).find("#normal");
    let firstClick = normalIcon.is(":visible");
    if (firstClick) {
        normalIcon.hide();
        confirmIcon.show();
    } else {
        normalIcon.show();
        confirmIcon.hide();
        ipc.send('delete-template', currentTemplateUUID);
        handleTemplateBackClick();
    }
}

ipc.on('template-deleted', function(event, uuid) {
    let panel = templateList.find('.panel[uuid="' + uuid + '"]');
    panel.remove();
});

function handlePropertyDeleteClick() {
    let confirmIcon = $(this).find("#confirm");
    let normalIcon = $(this).find("#normal");
    let firstClick = normalIcon.is(":visible");
    if (firstClick) {
        normalIcon.hide();
        confirmIcon.show();
    } else {
        normalIcon.show();
        confirmIcon.hide();
        ipc.send('delete-property', currentTemplateUUID, currentPropertyUUID);
        handlePropertyBackClick();
    }
}

ipc.on('property-deleted', function(event, templateUUID, propertyUUID) {
    let panel = templateEdit.find('.panel[uuid="' + propertyUUID + '"]');
    panel.remove();
});

function handleTemplateBackClick() {
    state = 'list';
    currentTemplateUUID = null;
    templateList.show();
    templateEdit.hide();
}

function handleNewPropertyClick() {
    ipc.send('create-property', currentTemplateUUID);
}

ipc.on('property-created', function(event, template, property) {
    if (state == 'template' && template.uuid == currentTemplateUUID) {
        addProperty(property);
    }
});

function addProperty(data) {
    let property = templateEdit.find('#template').find('div').first().clone();
    templateEdit.find('#list').append(property);
    property.attr('uuid', data.uuid);
    property.find('#name').text(data.name);
    property.find('#type').text(data.type);
    property.find('#edit').click(handleEditProperty);
}

function handleEditProperty() {
    state = 'property';
    currentPropertyUUID = $(this).closest('.panel').attr('uuid');

    updateProperty();

    templateEdit.hide();
    propertyEdit.show();
}

function updateProperty() {
    let text = propertyEdit.find("#text");
    let checkbox = propertyEdit.find("#checkbox");
    let template = ipc.sendSync('request-template', currentTemplateUUID);
    let property = template.properties[currentPropertyUUID];

    propertyEdit.find("#name").val(property.name);
    propertyEdit.find("#type").val(property.type);

    if (property.type == 'boolean') {
        text.hide();
        checkbox.show();
        checkbox.find('#defaultValue').attr('checked', property.defaultValue);
    } else {
        text.show();
        checkbox.hide();
        text.find("#defaultValue").val(property.defaultValue);
    }
}

ipc.on('property-updated', function(event, template, property) {
    // update property in template edit
    let panel = templateEdit.find('.panel[uuid="' + property.uuid + '"]');
    panel.find('#name').text(property.name);
    panel.find('#type').text(property.type);
    // update property in property edit
    if (state == 'property' && property.uuid == currentPropertyUUID) {
        updateProperty();
    }
});

function handlePropertyBackClick() {
    state = 'template';
    currentPropertyUUID = null;
    templateEdit.show();
    propertyEdit.hide();
}
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import ReactDOM, {unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer} from 'react-dom'
import assign from 'object-assign'

let FG_SIZE = 8
let BG_SIZE = 9

class Card extends React.Component {
  static propTypes = {
    active: PropTypes.bool,
    position: PropTypes.oneOf([
      'top',
      'right',
      'bottom',
      'left'
    ]),
    arrow: PropTypes.oneOf([
      null,
      'center',
      'top',
      'right',
      'bottom',
      'left'
    ]),
    style: PropTypes.object
  }
  static defaultProps = {
    active: false,
    position: 'right',
    arrow: null,
    style: {style: {}, arrowStyle: {}}
  }
  state = {
    hover: false,
    transition: 'opacity',
    width: 0,
    height: 0
  }
  margin = 15
  defaultArrowStyle = {
    color: '#fff',
    borderColor: 'rgba(0,0,0,.4)'
  }
  getGlobalStyle() {
    if (!this.props.parentEl) {
      return {display: 'none'}
    }

    let style = {
      position: 'absolute',
      padding: '5px',
      background: '#fff',
      boxShadow: '0 0 8px rgba(0,0,0,.3)',
      borderRadius: '3px',
      transition: `${this.state.transition} .3s ease-in-out, visibility .3s ease-in-out`,
      opacity: this.state.hover || this.props.active ? 1 : 0,
      visibility: this.state.hover || this.props.active ? 'visible' : 'hidden',
      zIndex: 50
    }

    assign(style, this.getStyle(this.props.position, this.props.arrow))

    return this.mergeStyle(style, this.props.style.style)
  }
  getBaseArrowStyle() {
    return {
      position: 'absolute',
      content: '""',
      transition: 'all .3s ease-in-out'
    }
  }
  getArrowStyle() {
    let fgStyle = this.getBaseArrowStyle()
    let bgStyle = this.getBaseArrowStyle()
    fgStyle.zIndex = 60
    bgStyle.zIndex = 55

    let arrowStyle = assign(this.defaultArrowStyle, this.props.style.arrowStyle)
    let bgBorderColor = arrowStyle.borderColor ? arrowStyle.borderColor : 'transparent'

    let fgColorBorder = `10px solid ${arrowStyle.color}`
    let fgTransBorder = `${FG_SIZE}px solid transparent`
    let bgColorBorder = `11px solid ${bgBorderColor}`
    let bgTransBorder = `${BG_SIZE}px solid transparent`

    let {position, arrow} = this.props

    if (position === 'left' || position === 'right') {
      fgStyle.top = '50%'
      fgStyle.borderTop = fgTransBorder
      fgStyle.borderBottom = fgTransBorder
      fgStyle.marginTop = -7

      bgStyle.borderTop = bgTransBorder
      bgStyle.borderBottom = bgTransBorder
      bgStyle.top = '50%'
      bgStyle.marginTop = -8

      if (position === 'left') {
        fgStyle.right = -10
        fgStyle.borderLeft = fgColorBorder
        bgStyle.right = -11
        bgStyle.borderLeft = bgColorBorder
      }
      else {
        fgStyle.left = -10
        fgStyle.borderRight = fgColorBorder
        bgStyle.left = -11
        bgStyle.borderRight = bgColorBorder
      }

      if (arrow === 'top') {
        fgStyle.top = this.margin
        bgStyle.top = this.margin
      }
      if (arrow === 'bottom') {
        fgStyle.top = null
        fgStyle.bottom = this.margin - 7
        bgStyle.top = null
        bgStyle.bottom = this.margin - 8
      }
    }
    else {
      fgStyle.left = Math.round((this.state.width / 2) - FG_SIZE)
      fgStyle.borderLeft = fgTransBorder
      fgStyle.borderRight = fgTransBorder
      fgStyle.marginLeft = 0
      bgStyle.left = fgStyle.left - 1
      bgStyle.borderLeft = bgTransBorder
      bgStyle.borderRight = bgTransBorder
      bgStyle.marginLeft = 0

      if (position === 'top') {
        fgStyle.bottom = -10
        fgStyle.borderTop = fgColorBorder
        bgStyle.bottom = -11
        bgStyle.borderTop = bgColorBorder
      }
      else {
        fgStyle.top = -10
        fgStyle.borderBottom = fgColorBorder
        bgStyle.top = -11
        bgStyle.borderBottom = bgColorBorder
      }

      if (arrow === 'right') {
        fgStyle.left = null
        fgStyle.right = this.margin + 1 - FG_SIZE
        bgStyle.left = null
        bgStyle.right = this.margin - FG_SIZE
      }
      if (arrow === 'left') {
        fgStyle.left = this.margin + 1 - FG_SIZE
        bgStyle.left = this.margin - FG_SIZE
      }
    }

    let {color, borderColor, ...propsArrowStyle} = this.props.style.arrowStyle

    return {
      fgStyle: this.mergeStyle(fgStyle, propsArrowStyle),
      bgStyle: this.mergeStyle(bgStyle, propsArrowStyle)
    }
  }
  mergeStyle(style, theme) {
    if (theme) {
      let {position, top, left, right, bottom, marginLeft, marginRight, ...validTheme} = theme

      return assign(style, validTheme)
    }

    return style
  }
  getStyle(position, arrow) {
    let parent = this.props.parentEl
    let tooltipPosition = parent.getBoundingClientRect()
    let scrollY = (window.scrollY !== undefined) ? window.scrollY : window.pageYOffset
    let scrollX = (window.scrollX !== undefined) ? window.scrollX : window.pageXOffset
    let top = scrollY + tooltipPosition.top
    let left = scrollX + tooltipPosition.left
    let style = {}

    const stylesFromPosition = {
      left: () => {
        style.top = top + parent.offsetHeight / 2 - this.state.height / 2
        style.left = left - this.state.width - this.margin
      },
      right: () => {
        style.top = top + parent.offsetHeight / 2 - this.state.height / 2
        style.left = left + parent.offsetWidth + this.margin
      },
      top: () => {
        style.left = left - this.state.width / 2 + parent.offsetWidth / 2
        style.top = top - this.state.height - this.margin
      },
      bottom: () => {
        style.left = left - this.state.width / 2 + parent.offsetWidth / 2
        style.top = top + parent.offsetHeight + this.margin
      },
    }

    const stylesFromArrow = {
      left: () => {
        style.left = left + parent.offsetWidth / 2 - this.margin
      },
      right: () => {
        style.left = left - this.state.width + parent.offsetWidth / 2 + this.margin
      },
      top: () => {
        style.top = top + parent.offsetHeight / 2 - this.margin
      },
      bottom: () => {
        style.top = top + parent.offsetHeight / 2 - this.state.height + this.margin
      },
    }

    executeFunctionIfExist(stylesFromPosition, position)
    executeFunctionIfExist(stylesFromArrow, arrow)

    return style
  }
  checkWindowPosition(style, arrowStyle) {
    if (this.props.position === 'top' || this.props.position === 'bottom') {
      if (style.left < 0) {
        const parent = this.props.parentEl
        if (parent) {
          const tooltipWidth = this.state.width
          let bgStyleRight = arrowStyle.bgStyle.right
          // For arrow = center
          if (!bgStyleRight) {
            bgStyleRight = (tooltipWidth / 2) - BG_SIZE
          }
          const newBgRight = Math.round(bgStyleRight - style.left + this.margin)
          arrowStyle = Object.assign({}, arrowStyle, {
            bgStyle: Object.assign({}, arrowStyle.bgStyle, {right: newBgRight, left: null}),
            fgStyle: Object.assign({}, arrowStyle.fgStyle, {right: newBgRight + 1, left: null})
          })
        }
        style.left = this.margin
      }
      else {
        let rightOffset = style.left + this.state.width - window.innerWidth
        if (rightOffset > 0) {
          let originalLeft = style.left
          style.left = window.innerWidth - this.state.width - this.margin
          arrowStyle.fgStyle.marginLeft += originalLeft - style.left
          arrowStyle.bgStyle.marginLeft += originalLeft - style.left
        }
      }
    }
    return {style, arrowStyle}
  }
  handleMouseEnter() {
    this.props.active && this.setState({hover: true})
  }
  handleMouseLeave() {
    this.setState({hover: false})
  }
  componentDidMount() {
    this.updateSize()
  }
  componentWillReceiveProps() {
    this.setState({transition: this.state.hover || this.props.active ? 'all' : 'opacity'}, () => {
      this.updateSize()
    })
  }
  updateSize() {
    let self = ReactDOM.findDOMNode(this)
    this.setState({
      width: self.offsetWidth,
      height: self.offsetHeight
    })
  }
  render() {
    let {style, arrowStyle} = this.checkWindowPosition(this.getGlobalStyle(), this.getArrowStyle())

    return (

        {this.props.arrow ? (

)
          : null
        }
        {this.props.children}

    )
  }
}

var portalNodes = {}

export default class ToolTip extends React.Component {
  static propTypes = {
    parent: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.object
    ]).isRequired,
    active: PropTypes.bool,
    group: PropTypes.string,
    tooltipTimeout: PropTypes.number
  }
  static defaultProps = {
    active: false,
    group: 'main',
    tooltipTimeout: 500
  }
  componentDidMount() {
    if (!this.props.active) {
      return
    }

    this.renderPortal(this.props)
  }
  componentWillReceiveProps(nextProps) {
    if ((!portalNodes[this.props.group] && !nextProps.active) ||
      (!this.props.active && !nextProps.active)) {
      return
    }

    let props = assign({}, nextProps)
    let newProps = assign({}, nextProps)

    if (portalNodes[this.props.group] && portalNodes[this.props.group].timeout) {
      clearTimeout(portalNodes[this.props.group].timeout)
    }

    if (this.props.active && !props.active) {
      newProps.active = true
      portalNodes[this.props.group].timeout = setTimeout(() => {
        props.active = false
        this.renderPortal(props)
      }, this.props.tooltipTimeout)
    }

    this.renderPortal(newProps)
  }
  componentWillUnmount() {
    if (portalNodes[this.props.group]) {
      ReactDOM.unmountComponentAtNode(portalNodes[this.props.group].node)
      clearTimeout(portalNodes[this.props.group].timeout)
    }
  }
  createPortal() {
    portalNodes[this.props.group] = {
      node: document.createElement('div'),
      timeout: false
    }
    portalNodes[this.props.group].node.className = 'ToolTipPortal'
    document.body.appendChild(portalNodes[this.props.group].node)
  }
  renderPortal(props) {
    if (!portalNodes[this.props.group]) {
      this.createPortal()
    }
    let {parent, ...other} = props
    let parentEl = typeof parent === 'string' ? document.querySelector(parent) : parent
    renderSubtreeIntoContainer(this, <Card parentEl={parentEl} {...other}/>, portalNodes[this.props.group].node)
  }
  shouldComponentUpdate() {
    return false
  }
  render() {
    return null
  }
}

const executeFunctionIfExist = (object, key) => {
  if (Object.prototype.hasOwnProperty.call(object, key)){
    object[key]()
  }
}
<commit_msg>use const instead let for constant fg & bg size value<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import ReactDOM, {unstable_renderSubtreeIntoContainer as renderSubtreeIntoContainer} from 'react-dom'
import assign from 'object-assign'

const FG_SIZE = 8
const BG_SIZE = 9

class Card extends React.Component {
  static propTypes = {
    active: PropTypes.bool,
    position: PropTypes.oneOf([
      'top',
      'right',
      'bottom',
      'left'
    ]),
    arrow: PropTypes.oneOf([
      null,
      'center',
      'top',
      'right',
      'bottom',
      'left'
    ]),
    style: PropTypes.object
  }
  static defaultProps = {
    active: false,
    position: 'right',
    arrow: null,
    style: {style: {}, arrowStyle: {}}
  }
  state = {
    hover: false,
    transition: 'opacity',
    width: 0,
    height: 0
  }
  margin = 15
  defaultArrowStyle = {
    color: '#fff',
    borderColor: 'rgba(0,0,0,.4)'
  }
  getGlobalStyle() {
    if (!this.props.parentEl) {
      return {display: 'none'}
    }

    let style = {
      position: 'absolute',
      padding: '5px',
      background: '#fff',
      boxShadow: '0 0 8px rgba(0,0,0,.3)',
      borderRadius: '3px',
      transition: `${this.state.transition} .3s ease-in-out, visibility .3s ease-in-out`,
      opacity: this.state.hover || this.props.active ? 1 : 0,
      visibility: this.state.hover || this.props.active ? 'visible' : 'hidden',
      zIndex: 50
    }

    assign(style, this.getStyle(this.props.position, this.props.arrow))

    return this.mergeStyle(style, this.props.style.style)
  }
  getBaseArrowStyle() {
    return {
      position: 'absolute',
      content: '""',
      transition: 'all .3s ease-in-out'
    }
  }
  getArrowStyle() {
    let fgStyle = this.getBaseArrowStyle()
    let bgStyle = this.getBaseArrowStyle()
    fgStyle.zIndex = 60
    bgStyle.zIndex = 55

    let arrowStyle = assign(this.defaultArrowStyle, this.props.style.arrowStyle)
    let bgBorderColor = arrowStyle.borderColor ? arrowStyle.borderColor : 'transparent'

    let fgColorBorder = `10px solid ${arrowStyle.color}`
    let fgTransBorder = `${FG_SIZE}px solid transparent`
    let bgColorBorder = `11px solid ${bgBorderColor}`
    let bgTransBorder = `${BG_SIZE}px solid transparent`

    let {position, arrow} = this.props

    if (position === 'left' || position === 'right') {
      fgStyle.top = '50%'
      fgStyle.borderTop = fgTransBorder
      fgStyle.borderBottom = fgTransBorder
      fgStyle.marginTop = -7

      bgStyle.borderTop = bgTransBorder
      bgStyle.borderBottom = bgTransBorder
      bgStyle.top = '50%'
      bgStyle.marginTop = -8

      if (position === 'left') {
        fgStyle.right = -10
        fgStyle.borderLeft = fgColorBorder
        bgStyle.right = -11
        bgStyle.borderLeft = bgColorBorder
      }
      else {
        fgStyle.left = -10
        fgStyle.borderRight = fgColorBorder
        bgStyle.left = -11
        bgStyle.borderRight = bgColorBorder
      }

      if (arrow === 'top') {
        fgStyle.top = this.margin
        bgStyle.top = this.margin
      }
      if (arrow === 'bottom') {
        fgStyle.top = null
        fgStyle.bottom = this.margin - 7
        bgStyle.top = null
        bgStyle.bottom = this.margin - 8
      }
    }
    else {
      fgStyle.left = Math.round((this.state.width / 2) - FG_SIZE)
      fgStyle.borderLeft = fgTransBorder
      fgStyle.borderRight = fgTransBorder
      fgStyle.marginLeft = 0
      bgStyle.left = fgStyle.left - 1
      bgStyle.borderLeft = bgTransBorder
      bgStyle.borderRight = bgTransBorder
      bgStyle.marginLeft = 0

      if (position === 'top') {
        fgStyle.bottom = -10
        fgStyle.borderTop = fgColorBorder
        bgStyle.bottom = -11
        bgStyle.borderTop = bgColorBorder
      }
      else {
        fgStyle.top = -10
        fgStyle.borderBottom = fgColorBorder
        bgStyle.top = -11
        bgStyle.borderBottom = bgColorBorder
      }

      if (arrow === 'right') {
        fgStyle.left = null
        fgStyle.right = this.margin + 1 - FG_SIZE
        bgStyle.left = null
        bgStyle.right = this.margin - FG_SIZE
      }
      if (arrow === 'left') {
        fgStyle.left = this.margin + 1 - FG_SIZE
        bgStyle.left = this.margin - FG_SIZE
      }
    }

    let {color, borderColor, ...propsArrowStyle} = this.props.style.arrowStyle

    return {
      fgStyle: this.mergeStyle(fgStyle, propsArrowStyle),
      bgStyle: this.mergeStyle(bgStyle, propsArrowStyle)
    }
  }
  mergeStyle(style, theme) {
    if (theme) {
      let {position, top, left, right, bottom, marginLeft, marginRight, ...validTheme} = theme

      return assign(style, validTheme)
    }

    return style
  }
  getStyle(position, arrow) {
    let parent = this.props.parentEl
    let tooltipPosition = parent.getBoundingClientRect()
    let scrollY = (window.scrollY !== undefined) ? window.scrollY : window.pageYOffset
    let scrollX = (window.scrollX !== undefined) ? window.scrollX : window.pageXOffset
    let top = scrollY + tooltipPosition.top
    let left = scrollX + tooltipPosition.left
    let style = {}

    const stylesFromPosition = {
      left: () => {
        style.top = top + parent.offsetHeight / 2 - this.state.height / 2
        style.left = left - this.state.width - this.margin
      },
      right: () => {
        style.top = top + parent.offsetHeight / 2 - this.state.height / 2
        style.left = left + parent.offsetWidth + this.margin
      },
      top: () => {
        style.left = left - this.state.width / 2 + parent.offsetWidth / 2
        style.top = top - this.state.height - this.margin
      },
      bottom: () => {
        style.left = left - this.state.width / 2 + parent.offsetWidth / 2
        style.top = top + parent.offsetHeight + this.margin
      },
    }

    const stylesFromArrow = {
      left: () => {
        style.left = left + parent.offsetWidth / 2 - this.margin
      },
      right: () => {
        style.left = left - this.state.width + parent.offsetWidth / 2 + this.margin
      },
      top: () => {
        style.top = top + parent.offsetHeight / 2 - this.margin
      },
      bottom: () => {
        style.top = top + parent.offsetHeight / 2 - this.state.height + this.margin
      },
    }

    executeFunctionIfExist(stylesFromPosition, position)
    executeFunctionIfExist(stylesFromArrow, arrow)

    return style
  }
  checkWindowPosition(style, arrowStyle) {
    if (this.props.position === 'top' || this.props.position === 'bottom') {
      if (style.left < 0) {
        const parent = this.props.parentEl
        if (parent) {
          const tooltipWidth = this.state.width
          let bgStyleRight = arrowStyle.bgStyle.right
          // For arrow = center
          if (!bgStyleRight) {
            bgStyleRight = (tooltipWidth / 2) - BG_SIZE
          }
          const newBgRight = Math.round(bgStyleRight - style.left + this.margin)
          arrowStyle = Object.assign({}, arrowStyle, {
            bgStyle: Object.assign({}, arrowStyle.bgStyle, {right: newBgRight, left: null}),
            fgStyle: Object.assign({}, arrowStyle.fgStyle, {right: newBgRight + 1, left: null})
          })
        }
        style.left = this.margin
      }
      else {
        let rightOffset = style.left + this.state.width - window.innerWidth
        if (rightOffset > 0) {
          let originalLeft = style.left
          style.left = window.innerWidth - this.state.width - this.margin
          arrowStyle.fgStyle.marginLeft += originalLeft - style.left
          arrowStyle.bgStyle.marginLeft += originalLeft - style.left
        }
      }
    }
    return {style, arrowStyle}
  }
  handleMouseEnter() {
    this.props.active && this.setState({hover: true})
  }
  handleMouseLeave() {
    this.setState({hover: false})
  }
  componentDidMount() {
    this.updateSize()
  }
  componentWillReceiveProps() {
    this.setState({transition: this.state.hover || this.props.active ? 'all' : 'opacity'}, () => {
      this.updateSize()
    })
  }
  updateSize() {
    let self = ReactDOM.findDOMNode(this)
    this.setState({
      width: self.offsetWidth,
      height: self.offsetHeight
    })
  }
  render() {
    let {style, arrowStyle} = this.checkWindowPosition(this.getGlobalStyle(), this.getArrowStyle())

    return (

        {this.props.arrow ? (

)
          : null
        }
        {this.props.children}

    )
  }
}

var portalNodes = {}

export default class ToolTip extends React.Component {
  static propTypes = {
    parent: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.object
    ]).isRequired,
    active: PropTypes.bool,
    group: PropTypes.string,
    tooltipTimeout: PropTypes.number
  }
  static defaultProps = {
    active: false,
    group: 'main',
    tooltipTimeout: 500
  }
  componentDidMount() {
    if (!this.props.active) {
      return
    }

    this.renderPortal(this.props)
  }
  componentWillReceiveProps(nextProps) {
    if ((!portalNodes[this.props.group] && !nextProps.active) ||
      (!this.props.active && !nextProps.active)) {
      return
    }

    let props = assign({}, nextProps)
    let newProps = assign({}, nextProps)

    if (portalNodes[this.props.group] && portalNodes[this.props.group].timeout) {
      clearTimeout(portalNodes[this.props.group].timeout)
    }

    if (this.props.active && !props.active) {
      newProps.active = true
      portalNodes[this.props.group].timeout = setTimeout(() => {
        props.active = false
        this.renderPortal(props)
      }, this.props.tooltipTimeout)
    }

    this.renderPortal(newProps)
  }
  componentWillUnmount() {
    if (portalNodes[this.props.group]) {
      ReactDOM.unmountComponentAtNode(portalNodes[this.props.group].node)
      clearTimeout(portalNodes[this.props.group].timeout)
    }
  }
  createPortal() {
    portalNodes[this.props.group] = {
      node: document.createElement('div'),
      timeout: false
    }
    portalNodes[this.props.group].node.className = 'ToolTipPortal'
    document.body.appendChild(portalNodes[this.props.group].node)
  }
  renderPortal(props) {
    if (!portalNodes[this.props.group]) {
      this.createPortal()
    }
    let {parent, ...other} = props
    let parentEl = typeof parent === 'string' ? document.querySelector(parent) : parent
    renderSubtreeIntoContainer(this, <Card parentEl={parentEl} {...other}/>, portalNodes[this.props.group].node)
  }
  shouldComponentUpdate() {
    return false
  }
  render() {
    return null
  }
}

const executeFunctionIfExist = (object, key) => {
  if (Object.prototype.hasOwnProperty.call(object, key)){
    object[key]()
  }
}
<|endoftext|>


<commit_before>import { PVec } from "./pvec";
import { TVec } from "./tvec";

TVec.prototype.asPersistent = function (){
	this.id = null;
	return new PVec(this.size, this.shift, this.root, this.compressedTail());
};
PVec.prototype.asTransient = function () {
	return new TVec(this.size, this.shift, this.root, this.tail);
};

export const empty = new PVec();
export function isPVec(vec){
	return !!vec && vec.__isVec_Sentinel;
}
export { PVec, TVec };
<commit_msg>fixed slicing params<commit_after>import { PVec } from "./pvec";
import { TVec } from "./tvec";

TVec.prototype.asPersistent = function (){
	this.id = null;
	return new PVec(this.origin, this.capacity, this.shift, this.root, this.compressedTail());
};
PVec.prototype.asTransient = function () {
	return new TVec(this.origin, this.capacity, this.shift, this.root, this.tail);
};

export const empty = new PVec();
export function isPVec(vec){
	return !!vec && vec.__isVec_Sentinel;
}
export { PVec, TVec };
<|endoftext|>


<commit_before>'use strict';
(() => {
  module.exports = (gulp, plugins, config) => {
    return () => {
      console.log(`${config.patternsPath}/**/*.js`);
      return gulp.src([`${config.patternsPath}/**/*.js`, `!${config.patternsPath}/**/*.min.js`])
        .pipe(plugins.changed(config.buildPath))
        .pipe(plugins.plumber({
          errorHandler: plugins.notify.onError('Error: <%= error.message %>')
        }))
        .pipe(plugins.rename({ suffix: '.min' }))
        .pipe(plugins.sourcemaps.init())
        .pipe(plugins.babel({
          presets: [
            ['env', { 'modules': false }]
          ],
          ignore: [
            // Keep these files from breaking until they're refactored to ES6 code.
            `${config.patternsPath}/components/dialogs/**/*.js`
          ]
        }))
        .pipe(plugins.uglify())
        .pipe(plugins.size(config.sizeOptions))
        .pipe(plugins.sourcemaps.write('.', { sourceRoot: '.' }))
        .pipe(gulp.dest(config.buildPath))
      ;
    };
  };
})();
<commit_msg>revert(Buttons): Exclude dynamic buttons from ES6ification.<commit_after>'use strict';
(() => {
  module.exports = (gulp, plugins, config) => {
    return () => {
      console.log(`${config.patternsPath}/**/*.js`);
      return gulp.src([`${config.patternsPath}/**/*.js`, `!${config.patternsPath}/**/*.min.js`])
        .pipe(plugins.changed(config.buildPath))
        .pipe(plugins.plumber({
          errorHandler: plugins.notify.onError('Error: <%= error.message %>')
        }))
        .pipe(plugins.rename({ suffix: '.min' }))
        .pipe(plugins.sourcemaps.init())
        .pipe(plugins.babel({
          presets: [
            ['env', { 'modules': false }]
          ],
          ignore: [
            // Keep these files from breaking until they're refactored to ES6 code.
            `${config.patternsPath}/components/dialogs/**/*.js`,
            `${config.patternsPath}/components/buttons/rocketbelt.dynamic-button.js`
          ]
        }))
        .pipe(plugins.uglify())
        .pipe(plugins.size(config.sizeOptions))
        .pipe(plugins.sourcemaps.write('.', { sourceRoot: '.' }))
        .pipe(gulp.dest(config.buildPath))
      ;
    };
  };
})();
<|endoftext|>


<commit_before>export Icon from './components/base/icons/Icon';

export Alert from './components/containers/alert/Alert';
export Drawer from './components/containers/drawer/Drawer';
export Fieldset from './components/containers/fieldset/Fieldset';
export Tooltip from './components/containers/tooltip/Tooltip';
export PopoverLink from './components/containers/popover/PopoverLink';

export Textbox from './components/controls/textbox/Textbox';
export Button from './components/controls/buttons/Button';
export RadioGroup from './components/controls/radio-buttons/RadioGroup';
export Dropdown from './components/controls/dropdown/Dropdown';
export DatePicker from './components/controls/datepicker/DatePicker';
<commit_msg>added export<commit_after>export Icon from './components/base/icons/Icon';

export Alert from './components/containers/alert/Alert';
export Drawer from './components/containers/drawer/Drawer';
export Fieldset from './components/containers/fieldset/Fieldset';
export Tooltip from './components/containers/tooltip/Tooltip';
export PopoverLink from './components/containers/popover/PopoverLink';
export Modal from './components/containers/modal/Modal';

export Textbox from './components/controls/textbox/Textbox';
export Button from './components/controls/buttons/Button';
export RadioGroup from './components/controls/radio-buttons/RadioGroup';
export Dropdown from './components/controls/dropdown/Dropdown';
export DatePicker from './components/controls/datepicker/DatePicker';
<|endoftext|>


<commit_before>msuParts["wikilink"] = function(ScratchUserscript){
	var settingsDlg = $("

");

	ScratchUserscript.registerPart("Wiki Link", "Adds a link to the wiki in the top bar.", settingsDlg);

	var isEnabled = ScratchUserscript.isPartEnabled("wikilink");

	var settings = {
		name: "Wiki"
	};

	settings = ScratchUserscript.readSetting("wikilink", "settings", settings);

	updateChanges = function(){
		settings.name = $("#wiki-name").val();
		ScratchUserscript.writeSetting("wikilink", "settings", settings);

		if(isEnabled) {
			settings = ScratchUserscript.readSetting("wikilink", "settings", settings); //Update settings
			$(".site-nav li a[href='http://wiki.scratch.mit.edu/']").html(settings.name); //Auto-update button
		}
	};

	settingsDlg.append('Button Name: <input type="text" id="wiki-name" value="' + settings.name + '">');

	settingsDlg.find("input").bind("change cut paste keyup", updateChanges);

	if(!isEnabled) return;

	// begin actual script
	$('.site-nav li:nth-of-type(3)').after("
" + settings.name + "
");
};
<commit_msg>Reverted last commit<commit_after>msuParts["colortheme"] = function(ScratchUserscript){
	var settingsDlg = $("

");

	ScratchUserscript.registerPart("Color Theme", "Changes the colors of the Scratch page.", settingsDlg);

	var isEnabled = ScratchUserscript.isPartEnabled("colortheme");

	var custom = {
		main: "#25B36A",
		dark: "#128C4D",
		text: "#FFFFFF"
	};

	var preset = {
		green:   ["#99CD4E", "#7EA840", "#fff"],
		orange:  ["#ED6332", "#AD4723", "#fff"],
		seafoam: ["#25B36A", "#128C4D", "#fff"],
		purple:  ["#8943E6", "#591DA8", "#fff"],
		pink:    ["#E34BA1", "#A6246E", "#fff"],
		custom:  []
	};

	var settings = {
		none: true,
		green: false,
		seafoam: false,
		orange: false,
		purple: false,
		pink: false,
		custom: false
	};

	var labels = {
		none: "Default Scratch Theme",
		green: "Green",
		seafoam: "Sea Foam",
		orange: "Orange",
		purple: "Purple",
		pink: "Pink",
		custom: "Custom"
	};

	settings = ScratchUserscript.readSetting("colortheme", "settings", settings);
	if(ScratchUserscript.MODE_DEV) console.log(settings);
	custom = ScratchUserscript.readSetting("colortheme", "custom", custom);

	updateChanges = function(){
		for(x in settings){
			if($("#msu-ct"+x+"btn").is(":checked")){
				settings[x] = true;
			} else {
				settings[x] = false;
			}
		}
		custom['main'] = $("#msu-ct-main").val();
		custom['dark'] = $("#msu-ct-dark").val();
		custom['text'] = $("#msu-ct-text").val();
		ScratchUserscript.writeSetting("colortheme", "settings", settings);
		ScratchUserscript.writeSetting("colortheme", "custom", custom);
	};

	settingsDlg.append("Select a theme:
");

	for(x in settings){ // much more concise than adding individual settings one by one I would say
		settingsDlg.append("<input type='radio' name='msu-ct-radio' id='msu-ct"+x+"btn' /> \
			\
			"+labels[x]+"
");
		if(settings[x]) $("#msu-ct"+x+"btn").attr("checked", "checked");
	}
	settingsDlg.append("Custom colors (choose 'Custom'; hex colors only please):
");
	settingsDlg.append("Main color:<input type='text' id='msu-ct-main' />
");
	settingsDlg.append("Dark color:<input type='text' id='msu-ct-dark' />
");
	settingsDlg.append("Text color:<input type='text' id='msu-ct-text' />
");

	settingsDlg.find("input").bind("change cut paste keyup", updateChanges);

	$("#msu-ct-main").val(custom['main']);
	$("#msu-ct-dark").val(custom['dark']);
	$("#msu-ct-text").val(custom['text']);

	if(!isEnabled || settings.none) return;

	// begin actual script
	preset.custom = [custom['main'], custom['dark'], custom['text']];
	var sel = "custom";
	for(x in settings){
		if(settings.hasOwnProperty(x) && settings[x]){
			sel = x;
		}
	}
	var css = GM_getResourceText("theme");
	css = css.replace(/\$([a-zA-Z0-9\-]+)/g, function(match, p1, offset, string){
		if(p1.indexOf("main-color") > -1) return preset[sel][0];
		if(p1.indexOf("main-dark") > -1) return preset[sel][1];
		return preset[sel][2];
	});

	GM_addStyle(css);
};
<|endoftext|>