<commit_before>import React, { Component } from 'react';
import '../App.css';
import TimerForm from './Timer/TimerForm.js';
import TimerContainer from './Timer/TimerContainer.js';
import AlertsContainer from './Alerts/AlertsContainer.js';
import AlertRow from './Alerts/AlertRow.js';
import AlertEditForm from './Alerts/AlertEditForm.js';
import {toTime, totalSeconds, formatSeconds} from '../lib/Time.js';

class Timer extends Component{

  constructor(props){
    super(props);
    this.state = {
      initialTime: null,
      time: null,
      minutes: 0,
      seconds: 0,
      intervalID: null,
      alerts:[],
      triggeredAlerts: [],
      errorText: "",
      editing: null,
      editingIndex: null,
    }
    this.calculateTime = this.calculateTime.bind(this)
    this.timerFormSubmit = this.timerFormSubmit.bind(this)
    this.createAlert = this.createAlert.bind(this)
    this.updateAlert = this.updateAlert.bind(this)
    this.toggleTimer = this.toggleTimer.bind(this)
    this.resetTimer = this.resetTimer.bind(this)
    this.createAlert = this.createAlert.bind(this)
    this.deleteAlert = this.deleteAlert.bind(this)
    this.editAlert = this.editAlert.bind(this)
    this.updateAlert = this.updateAlert.bind(this)
  }

  componentDidMount(){
    if(this.state.time !== null){
      this.initializeTimer()
    }
  }

  calculateTime(){
    let time = this.state.time
    if(time === this.state.initialTime){
      time -= 1000
    }
    const minutes = Math.floor(time / 60000);
    let seconds = (time % 60000) / 1000;
    seconds = formatSeconds(seconds)
    let alerts = this.state.alerts
    const firedAlerts = alerts.filter((a) => {
      return toTime(a.minutes, a.seconds) === toTime(minutes, seconds)
    })
    alerts.splice(0, firedAlerts.length)
    let triggeredAlerts = this.state.triggeredAlerts
    triggeredAlerts = triggeredAlerts.concat(firedAlerts)
    this.setState({
      minutes: minutes,
      seconds: seconds,
      time: time - 1000,
      alerts: alerts,
      triggeredAlerts: triggeredAlerts
    }, this.timerEnd())
  }

  createAlert(minutes, seconds, desc){
    let alert = {
      minutes: minutes,
      seconds: seconds,
      description: desc
    }
    alert.seconds = formatSeconds(alert.seconds)
    let alerts = this.state.alerts
    alerts.push(alert)
    alerts.sort((a, b) => {
      const totalSeconds = (minutes, seconds) => {
        return (minutes * 60) + parseInt(seconds, 10)
      }
      if(totalSeconds(a.minutes, a.seconds) < totalSeconds(b.minutes, b.seconds)){
        return 1
      }
      if(totalSeconds(a.minutes, a.seconds) > totalSeconds(b.minutes, b.seconds)){
        return -1
      }
      return 0
    })
    this.setState({
      alerts: alerts,
      errorText: ""
    })
  }

  deleteAlert(index){
    this.state.alerts.splice(index, 1)
    this.setState({alerts: this.state.alerts})
  }

  editAlert(alert, index){
    this.setState({
      editing: alert,
      editingIndex: index
    })
  }

  timerFormSubmit(minutes){
    return (e) => {
      e.preventDefault()
      const ms = minutes * 60000
      this.setState({
        time: ms,
        initialTime: ms,
      }, this.initializeTimer)
    }
  }

  initializeTimer(){
    const minutes = Math.floor(this.state.time / 60000);
    let seconds = (this.state.time % 60000) / 1000;
    seconds = formatSeconds(seconds)
    this.setState({
      minutes: minutes,
      seconds: seconds
    })
  }

  resetTimer(){
    let triggeredAlerts = this.state.triggeredAlerts
    const alerts = triggeredAlerts.concat(this.state.alerts)
    triggeredAlerts = []
    this.setState({
      time: this.state.initialTime,
      alerts: alerts,
      triggeredAlerts: triggeredAlerts
    }, this.initializeTimer)
  }

  sortAlerts(alerts){
    return alerts.sort((a, b) => {
      if(totalSeconds(a.minutes, parseInt(a.seconds, 10)) < totalSeconds(b.minutes, parseInt(b.seconds, 10))){
        return 1
      }
      if(totalSeconds(a.minutes, parseInt(a.seconds, 10)) > totalSeconds(b.minutes, parseInt(b.seconds, 10))){
        return -1
      }
      return 0
    })
  }

  startTimer(){
    let intervalID = setInterval(this.calculateTime, 1000)
    this.setState({intervalID: intervalID});
  }

  stopTimer(){
    clearInterval(this.state.intervalID);
    this.setState({
      intervalID: null,
    });
  }

  timerEnd(){
    if(this.state.time === 0){
      this.stopTimer();
    }
  }

  toggleTimer(){
    if(this.state.intervalID){
      this.stopTimer()
    }else{
      if(this.state.time > 0){
        this.startTimer()
      }
    }
  }

  updateAlert(minutes, seconds, desc){
    let alert = {
      minutes: minutes,
      seconds: seconds,
      description: desc
    }
    if(alert.seconds < 10){
      alert.seconds = "0" + alert.seconds
    }
    let alerts = this.state.alerts
    alerts.splice(this.state.editingIndex, 1, alert)
    alerts = this.sortAlerts(alerts)
    this.setState({
      alerts: alerts,
      errorText: "",
      editing: null
    })
  }

  render(){

    let timerButtonClass = this.state.intervalID ? "stop" : "start"

    let timerButtonText = "START"

    let alertComponents = this.state.alerts.map((a, index) => {
      if(this.state.editing === null || index !== this.state.editingIndex){
        return(
          <AlertRow
            index={index}
            minutes={a.minutes}
            seconds={a.seconds}
            description={a.description}
            alert={a}
            deleteAlert={this.deleteAlert}
            editAlert={this.editAlert}
          />
        )
      }else{
        return(
          <AlertEditForm
            index={index}
            errorText={this.state.errorText}
            updateAlert={this.updateAlert}
            minutes={this.state.editing.minutes}
            seconds={this.state.editing.seconds}
            description={this.state.editing.description}
            maxTime={parseInt(this.state.time, 10)}
            maxMinutes={parseInt(this.state.minutes, 10)}
          />
        )
      }
    })

    if(this.state.intervalID){
      timerButtonText = "STOP"
    }

    if(this.state.time !== null){
      return(

          <TimerContainer
            minutes={this.state.minutes}
            seconds={this.state.seconds}
            buttonClass={timerButtonClass}
            buttonText={timerButtonText}
            toggleTimer={this.toggleTimer}
            resetTimer={this.resetTimer}
          />
          <AlertsContainer
            errorText={this.state.errorText}
            alertComponents={alertComponents}
            createAlert={this.createAlert}
            timerMinutes={parseInt(this.state.minutes, 10)}
            timerTime={parseInt(this.state.time, 10)}
          />

      )
    }else{
      return(
        <TimerForm handleSubmit={this.timerFormSubmit} />
      )
    }
  }
}

export default Timer;<commit_msg>Clean up some bad practices<commit_after>import React, { Component } from 'react';
import '../App.css';
import TimerForm from './Timer/TimerForm.js';
import TimerContainer from './Timer/TimerContainer.js';
import AlertsContainer from './Alerts/AlertsContainer.js';
import AlertRow from './Alerts/AlertRow.js';
import AlertEditForm from './Alerts/AlertEditForm.js';
import {toTime, totalSeconds, formatSeconds} from '../lib/Time.js';

class Timer extends Component{

  constructor(props){
    super(props);
    this.state = {
      initialTime: null,
      time: null,
      minutes: 0,
      seconds: 0,
      intervalID: null,
      alerts:[],
      triggeredAlerts: [],
      errorText: "",
      editing: null,
      editingIndex: null,
    }
    this.calculateTime = this.calculateTime.bind(this)
    this.timerFormSubmit = this.timerFormSubmit.bind(this)
    this.createAlert = this.createAlert.bind(this)
    this.updateAlert = this.updateAlert.bind(this)
    this.toggleTimer = this.toggleTimer.bind(this)
    this.resetTimer = this.resetTimer.bind(this)
    this.createAlert = this.createAlert.bind(this)
    this.deleteAlert = this.deleteAlert.bind(this)
    this.editAlert = this.editAlert.bind(this)
    this.updateAlert = this.updateAlert.bind(this)
  }

  componentDidMount(){
    if(this.state.time !== null){
      this.initializeTimer()
    }
  }

  calculateTime(){
    let time = this.state.time
    if(time === this.state.initialTime){
      time -= 1000
    }
    const minutes = Math.floor(time / 60000);
    let seconds = (time % 60000) / 1000;
    seconds = formatSeconds(seconds)
    let alerts = this.state.alerts
    const firedAlerts = alerts.filter((a) => {
      return toTime(a.minutes, a.seconds) === toTime(minutes, seconds)
    })
    alerts.splice(0, firedAlerts.length)
    let triggeredAlerts = this.state.triggeredAlerts
    triggeredAlerts = triggeredAlerts.concat(firedAlerts)
    this.setState({
      minutes: minutes,
      seconds: seconds,
      time: time - 1000,
      alerts: alerts,
      triggeredAlerts: triggeredAlerts
    }, this.timerEnd())
  }

  createAlert(minutes, seconds, desc){
    let alert = {
      minutes: minutes,
      seconds: seconds,
      description: desc
    }
    alert.seconds = formatSeconds(alert.seconds)
    let alerts = this.state.alerts
    alerts.push(alert)
    alerts = this.sortAlerts(alerts)
    this.setState({
      alerts: alerts,
      errorText: ""
    })
  }

  deleteAlert(index){
    let alerts = this.state.alerts
    alerts.splice(index, 1)
    this.setState({alerts: alerts})
  }

  editAlert(alert, index){
    this.setState({
      editing: alert,
      editingIndex: index
    })
  }

  timerFormSubmit(minutes){
    return (e) => {
      e.preventDefault()
      const ms = minutes * 60000
      this.setState({
        time: ms,
        initialTime: ms,
      }, this.initializeTimer)
    }
  }

  initializeTimer(){
    const minutes = Math.floor(this.state.time / 60000);
    let seconds = (this.state.time % 60000) / 1000;
    seconds = formatSeconds(seconds)
    this.setState({
      minutes: minutes,
      seconds: seconds
    })
  }

  resetTimer(){
    let triggeredAlerts = this.state.triggeredAlerts
    const alerts = triggeredAlerts.concat(this.state.alerts)
    triggeredAlerts = []
    this.setState({
      time: this.state.initialTime,
      alerts: alerts,
      triggeredAlerts: triggeredAlerts
    }, this.initializeTimer)
  }

  sortAlerts(alerts){
    return alerts.sort((a, b) => {
      if(totalSeconds(a.minutes, parseInt(a.seconds, 10)) < totalSeconds(b.minutes, parseInt(b.seconds, 10))){
        return 1
      }
      if(totalSeconds(a.minutes, parseInt(a.seconds, 10)) > totalSeconds(b.minutes, parseInt(b.seconds, 10))){
        return -1
      }
      return 0
    })
  }

  startTimer(){
    let intervalID = setInterval(this.calculateTime, 1000)
    this.setState({intervalID: intervalID});
  }

  stopTimer(){
    clearInterval(this.state.intervalID);
    this.setState({
      intervalID: null,
    });
  }

  timerEnd(){
    if(this.state.time === 0){
      this.stopTimer();
    }
  }

  toggleTimer(){
    if(this.state.intervalID){
      this.stopTimer()
    }else{
      if(this.state.time > 0){
        this.startTimer()
      }
    }
  }

  updateAlert(minutes, seconds, desc){
    let alert = {
      minutes: minutes,
      seconds: seconds,
      description: desc
    }
    alert.seconds = formatSeconds(alert.seconds)
    let alerts = this.state.alerts
    alerts.splice(this.state.editingIndex, 1, alert)
    alerts = this.sortAlerts(alerts)
    this.setState({
      alerts: alerts,
      errorText: "",
      editing: null
    })
  }

  render(){

    let timerButtonClass = this.state.intervalID ? "stop" : "start"

    let timerButtonText = "START"

    let alertComponents = this.state.alerts.map((a, index) => {
      if(this.state.editing === null || index !== this.state.editingIndex){
        return(
          <AlertRow
            index={index}
            minutes={a.minutes}
            seconds={a.seconds}
            description={a.description}
            alert={a}
            deleteAlert={this.deleteAlert}
            editAlert={this.editAlert}
          />
        )
      }else{
        return(
          <AlertEditForm
            index={index}
            errorText={this.state.errorText}
            updateAlert={this.updateAlert}
            minutes={this.state.editing.minutes}
            seconds={this.state.editing.seconds}
            description={this.state.editing.description}
            maxTime={parseInt(this.state.time, 10)}
            maxMinutes={parseInt(this.state.minutes, 10)}
          />
        )
      }
    })

    if(this.state.intervalID){
      timerButtonText = "STOP"
    }

    if(this.state.time !== null){
      return(

          <TimerContainer
            minutes={this.state.minutes}
            seconds={this.state.seconds}
            buttonClass={timerButtonClass}
            buttonText={timerButtonText}
            toggleTimer={this.toggleTimer}
            resetTimer={this.resetTimer}
          />
          <AlertsContainer
            errorText={this.state.errorText}
            alertComponents={alertComponents}
            createAlert={this.createAlert}
            timerMinutes={parseInt(this.state.minutes, 10)}
            timerTime={parseInt(this.state.time, 10)}
          />

      )
    }else{
      return(
        <TimerForm handleSubmit={this.timerFormSubmit} />
      )
    }
  }
}

export default Timer;<|endoftext|>


<commit_before>/**
 * Created by AlexanderC on 10/27/15.
 */

'use strict';

import path from 'path';
import relativeFs from 'relative-fs';
import fse from 'fs-extra';
import fs from 'fs';
import {_extend as extend} from 'util';
import es6Promise from 'es6-promise';

// Fix missing Promise
es6Promise.polyfill();

export class S3FSRelativeFSExtender {
  /**
   * @param {Object|String} relativeFsPath
   */
  constructor(relativeFsPath) {
    let relativeFsObject = relativeFsPath;

    if (typeof relativeFsObject !== 'object') {
      relativeFsObject = relativeFs.relativeTo(relativeFsPath);
      relativeFsObject._rootFolder = relativeFsPath;
    } else if(!relativeFsObject.hasOwnProperty('_rootFolder')) {
      throw new Error('Missing _rootFolder property from RelativeFS object');
    }

    this._relativeFsObject = relativeFsObject;
  }

  /**
   * @returns {Object}
   */
  get relativeFs() {
    return this._relativeFsObject;
  }

  /**
   * @returns {Object}
   */
  get relativeFsExtended() {
    return extend(this._relativeFsObject, this.EXTEND_OBJECT);
  }

  /**
   * @returns {String}
   */
  get cwd() {
    return path.normalize(this._relativeFsObject._rootFolder || process.cwd());
  }

  /**
   * @returns {{getPath: Function, clone: Function, copyFile: Function, create: Function, destroy: Function, headObject: Function, listContents: Function, readdirp: Function, mkdirp: Function, rmdirp: Function}}
   * @constructor
   */
  get EXTEND_OBJECT() {
    let extendObject = {
      bucket: 'relative_fs',
      s3: {}, // @todo: mock `AWS.S3()`

      /**
       * @param {String} pathStr
       * @returns {String}
       *
       * @todo: return some fake bucket name? or the prefix?
       */
      getPath: (pathStr = '') => {
        return path.join(this.cwd, pathStr);
      },

      /**
       * @param {String} pathStr
       * @returns {Object}
       */
      clone: (pathStr) => {
        let newCwd = path.join(this.cwd, pathStr);

        return new S3FSRelativeFSExtender(newCwd).relativeFsExtended;
      },

      /**
       * @param {String} sourcePath
       * @param {String} destinationPath
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      copyFile: (sourcePath, destinationPath, callback = null) => {
        let absSourcePath = path.join(this.cwd, sourcePath);
        let absDestinationPath = path.join(this.cwd, destinationPath);

        if (callback) {
          fse.copy(absSourcePath, absDestinationPath, callback);
          return;
        }

        return new Promise((resolve, reject) => {
          fse.copy(absSourcePath, absDestinationPath, (error) => {
            if (error) {
              reject(error);
              return;
            }

            resolve();
          });
        });
      },

      /**
       * @param {Object} options
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      create: (options, callback = null) => {
        let error = new Error(`You do not have rights for this operation on bucket ${options.Bucket}`);

        if (callback) {
          callback(error);
          return;
        }

        return new Promise((resolve, reject) => {
          reject(error);
        });
      },

      /**
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      destroy: (callback = null) => {
        let error = new Error(`You do not have rights for this operation`);

        if (callback) {
          fse.remove(this.cwd, () => {
            callback(error);
          });
          return;
        }

        return new Promise((resolve, reject) => {
          fse.remove(this.cwd, (nativeError) => {
            if (nativeError) {
              reject(nativeError);
              return;
            }

            reject(error);
          });
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      headObject: (pathStr, callback = null) => {
        let responseObj = {
          AcceptRanges: '',
          Restore: '',
          LastModified: new Date().getTime(),
          ContentLength: 0,
          ETag: '55ad340609f4b302',
          MissingMeta: 0,
          VersionId: '55ad340609f4b302',
          StorageClass: 'REDUCED_REDUNDANCY',
        };

        if (callback) {
          callback(responseObj);
          return;
        }

        return new Promise((resolve) => {
          resolve(responseObj);
        });
      },

      /**
       * @param {String} pathStr
       * @param {String} marker
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      listContents: (pathStr, marker, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        let globResponseObj = {
          Marker: marker,
          IsTruncated: false,
          Contents: [],
          Name: this.cwd,
          Prefix: pathStr,
          Delimiter: '/',
          MaxKeys: 1000000000,
          CommonPrefixes: [],
          EncodingType: 'url',
        };

        let responseObj = {
          Key: null,
          LastModified: new Date().getTime(),
          Size: 0,
          ETag: '55ad340609f4b302',
          StorageClass: 'REDUCED_REDUNDANCY',
          Owner: {
            DisplayName: 's3fs',
            ID: '55ad340609f4b302',
          },
        };

        if (callback) {
          fse.walk(absPath)
            .on('data', (item) => {
              globResponseObj.Contents.push(extend(responseObj, {Key: item.path.substr(this.cwd.length)}));
            })
            .on('end', () => {
              callback(globResponseObj);
            });

          return;
        }

        return new Promise((resolve) => {
          fse.walk(absPath)
            .on('data', (item) => {
              globResponseObj.Contents.push(extend(responseObj, {Key: item.path.substr(this.cwd.length)}));
            })
            .on('end', () => {
              resolve(globResponseObj);
            });
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      readdirp: (pathStr, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        if (callback) {
          try {
            callback(null, S3FSRelativeFSExtender._readdirp(absPath, this.cwd));
          } catch(error) {
            callback(error, null);
          }

          return;
        }

        return new Promise((resolve, reject) => {
          try {
            resolve(S3FSRelativeFSExtender._readdirp(absPath, this.cwd));
          } catch(error) {
            reject(error);
          }
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      mkdirp: (pathStr, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        if (callback) {
          fse.mkdirp(absPath, callback);
          return;
        }

        return new Promise((resolve, reject) => {
          fse.mkdirp(absPath, (error) => {
            if (error) {
              reject(error);
              return;
            }

            resolve();
          });
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      rmdirp: (pathStr, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        if (callback) {
          fse.remove(absPath, callback);
          return;
        }

        return new Promise((resolve, reject) => {
          fse.remove(absPath, (error) => {
            if (error) {
              reject(error);
              return;
            }

            resolve();
          });
        });
      }
    };

    extendObject.copyDir = extendObject.copyFile;
    extendObject.delete = extendObject.create;
    extendObject.putBucketLifecycle = extendObject.create;

    return extendObject;
  }

  /**
   * @param {String} dir
   * @param {String} basePath
   * @returns {String[]}
   * @private
   */
  static _readdirp(dir, basePath = null) {
    let _files = [];

    let files = fs.readdirSync(dir);

    for (let i in files) {
      if (!files.hasOwnProperty(i)) {
        continue;
      }

      let file = path.join(dir, files[i]);

      _files.push(file);

      if (fs.statSync(file).isDirectory()) {
        _files = _files.concat(
          S3FSRelativeFSExtender._readdirp(file)
        );
      }
    }

    return basePath
      ? _files.map((file) => file.substr(basePath.length))
      : _files;
  }
}
<commit_msg>Add local s3fs path<commit_after>/**
 * Created by AlexanderC on 10/27/15.
 */

'use strict';

import path from 'path';
import relativeFs from 'relative-fs';
import fse from 'fs-extra';
import fs from 'fs';
import {_extend as extend} from 'util';
import es6Promise from 'es6-promise';

// Fix missing Promise
es6Promise.polyfill();

export class S3FSRelativeFSExtender {
  /**
   * @param {Object|String} relativeFsPath
   */
  constructor(relativeFsPath) {
    let relativeFsObject = relativeFsPath;

    if (typeof relativeFsObject !== 'object') {
      relativeFsObject = relativeFs.relativeTo(relativeFsPath);
      relativeFsObject._rootFolder = relativeFsPath;
    } else if(!relativeFsObject.hasOwnProperty('_rootFolder')) {
      throw new Error('Missing _rootFolder property from RelativeFS object');
    }

    this._relativeFsObject = relativeFsObject;
  }

  /**
   * @returns {Object}
   */
  get relativeFs() {
    return this._relativeFsObject;
  }

  /**
   * @returns {Object}
   */
  get relativeFsExtended() {
    return extend(this._relativeFsObject, this.EXTEND_OBJECT);
  }

  /**
   * @returns {String}
   */
  get cwd() {
    return path.normalize(this._relativeFsObject._rootFolder || process.cwd());
  }

  /**
   * @returns {{getPath: Function, clone: Function, copyFile: Function, create: Function, destroy: Function, headObject: Function, listContents: Function, readdirp: Function, mkdirp: Function, rmdirp: Function}}
   * @constructor
   */
  get EXTEND_OBJECT() {
    let extendObject = {
      bucket: 'relative_fs',
      path: '',
      s3: {}, // @todo: mock `AWS.S3()`

      /**
       * @param {String} pathStr
       * @returns {String}
       *
       * @todo: return some fake bucket name? or the prefix?
       */
      getPath: (pathStr = '') => {
        return path.join(this.cwd, pathStr);
      },

      /**
       * @param {String} pathStr
       * @returns {Object}
       */
      clone: (pathStr) => {
        let newCwd = path.join(this.cwd, pathStr);

        return new S3FSRelativeFSExtender(newCwd).relativeFsExtended;
      },

      /**
       * @param {String} sourcePath
       * @param {String} destinationPath
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      copyFile: (sourcePath, destinationPath, callback = null) => {
        let absSourcePath = path.join(this.cwd, sourcePath);
        let absDestinationPath = path.join(this.cwd, destinationPath);

        if (callback) {
          fse.copy(absSourcePath, absDestinationPath, callback);
          return;
        }

        return new Promise((resolve, reject) => {
          fse.copy(absSourcePath, absDestinationPath, (error) => {
            if (error) {
              reject(error);
              return;
            }

            resolve();
          });
        });
      },

      /**
       * @param {Object} options
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      create: (options, callback = null) => {
        let error = new Error(`You do not have rights for this operation on bucket ${options.Bucket}`);

        if (callback) {
          callback(error);
          return;
        }

        return new Promise((resolve, reject) => {
          reject(error);
        });
      },

      /**
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      destroy: (callback = null) => {
        let error = new Error(`You do not have rights for this operation`);

        if (callback) {
          fse.remove(this.cwd, () => {
            callback(error);
          });
          return;
        }

        return new Promise((resolve, reject) => {
          fse.remove(this.cwd, (nativeError) => {
            if (nativeError) {
              reject(nativeError);
              return;
            }

            reject(error);
          });
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      headObject: (pathStr, callback = null) => {
        let responseObj = {
          AcceptRanges: '',
          Restore: '',
          LastModified: new Date().getTime(),
          ContentLength: 0,
          ETag: '55ad340609f4b302',
          MissingMeta: 0,
          VersionId: '55ad340609f4b302',
          StorageClass: 'REDUCED_REDUNDANCY',
        };

        if (callback) {
          callback(responseObj);
          return;
        }

        return new Promise((resolve) => {
          resolve(responseObj);
        });
      },

      /**
       * @param {String} pathStr
       * @param {String} marker
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      listContents: (pathStr, marker, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        let globResponseObj = {
          Marker: marker,
          IsTruncated: false,
          Contents: [],
          Name: this.cwd,
          Prefix: pathStr,
          Delimiter: '/',
          MaxKeys: 1000000000,
          CommonPrefixes: [],
          EncodingType: 'url',
        };

        let responseObj = {
          Key: null,
          LastModified: new Date().getTime(),
          Size: 0,
          ETag: '55ad340609f4b302',
          StorageClass: 'REDUCED_REDUNDANCY',
          Owner: {
            DisplayName: 's3fs',
            ID: '55ad340609f4b302',
          },
        };

        if (callback) {
          fse.walk(absPath)
            .on('data', (item) => {
              globResponseObj.Contents.push(extend(responseObj, {Key: item.path.substr(this.cwd.length)}));
            })
            .on('end', () => {
              callback(globResponseObj);
            });

          return;
        }

        return new Promise((resolve) => {
          fse.walk(absPath)
            .on('data', (item) => {
              globResponseObj.Contents.push(extend(responseObj, {Key: item.path.substr(this.cwd.length)}));
            })
            .on('end', () => {
              resolve(globResponseObj);
            });
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      readdirp: (pathStr, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        if (callback) {
          try {
            callback(null, S3FSRelativeFSExtender._readdirp(absPath, this.cwd));
          } catch(error) {
            callback(error, null);
          }

          return;
        }

        return new Promise((resolve, reject) => {
          try {
            resolve(S3FSRelativeFSExtender._readdirp(absPath, this.cwd));
          } catch(error) {
            reject(error);
          }
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      mkdirp: (pathStr, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        if (callback) {
          fse.mkdirp(absPath, callback);
          return;
        }

        return new Promise((resolve, reject) => {
          fse.mkdirp(absPath, (error) => {
            if (error) {
              reject(error);
              return;
            }

            resolve();
          });
        });
      },

      /**
       * @param {String} pathStr
       * @param {Function|null} callback
       * @returns {Promise|undefined}
       */
      rmdirp: (pathStr, callback = null) => {
        let absPath = path.join(this.cwd, pathStr);

        if (callback) {
          fse.remove(absPath, callback);
          return;
        }

        return new Promise((resolve, reject) => {
          fse.remove(absPath, (error) => {
            if (error) {
              reject(error);
              return;
            }

            resolve();
          });
        });
      }
    };

    extendObject.copyDir = extendObject.copyFile;
    extendObject.delete = extendObject.create;
    extendObject.putBucketLifecycle = extendObject.create;

    return extendObject;
  }

  /**
   * @param {String} dir
   * @param {String} basePath
   * @returns {String[]}
   * @private
   */
  static _readdirp(dir, basePath = null) {
    let _files = [];

    let files = fs.readdirSync(dir);

    for (let i in files) {
      if (!files.hasOwnProperty(i)) {
        continue;
      }

      let file = path.join(dir, files[i]);

      _files.push(file);

      if (fs.statSync(file).isDirectory()) {
        _files = _files.concat(
          S3FSRelativeFSExtender._readdirp(file)
        );
      }
    }

    return basePath
      ? _files.map((file) => file.substr(basePath.length))
      : _files;
  }
}
<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular
        .module('atlasDetail')
        .component('atlasApiCall', {
            bindings: {
                endpoint: '@',
                partial: '@',
                useBrkObjectExpanded: '='
            },
            templateUrl: 'modules/detail/components/api-call/atlas-api-call.html',
            controller: AtlasApiCallController,
            controllerAs: 'vm'
        });

    AtlasApiCallController.$inject = ['$scope', 'api'];

    function AtlasApiCallController ($scope, api) {
        var vm = this;

        $scope.$watch('vm.endpoint', function (endpoint) {
            if (vm.useBrkObjectExpanded) {
                endpoint = endpoint.replace('brk/object', 'brk/object-expand');
            }

            vm.apiData = {};

            //Load the first page
            loadData(endpoint);

            //Load pages 2-n
            vm.loadMore = function () {
                loadData(vm.apiData.next);
            };
        });

        function loadData (endpoint) {
            api.getByUrl(endpoint).then(function (response) {
                var hasPagination = angular.isArray(response.results);

                if (hasPagination) {
                    if (angular.isUndefined(vm.apiData.results)) {
                        vm.apiData.results = [];
                    }

                    vm.apiData.count = response.count;
                    vm.apiData.results = vm.apiData.results.concat(response.results);
                    vm.apiData.next = response._links.next && response._links.next.href;
                } else {
                    vm.apiData.results = response;
                }
            });
        }
    }
})();
<commit_msg>Don't run if there is no known endpoint.<commit_after>(function () {
    'use strict';

    angular
        .module('atlasDetail')
        .component('atlasApiCall', {
            bindings: {
                endpoint: '@',
                partial: '@',
                useBrkObjectExpanded: '='
            },
            templateUrl: 'modules/detail/components/api-call/atlas-api-call.html',
            controller: AtlasApiCallController,
            controllerAs: 'vm'
        });

    AtlasApiCallController.$inject = ['$scope', 'api'];

    function AtlasApiCallController ($scope, api) {
        var vm = this;

        $scope.$watch('vm.endpoint', function (endpoint) {
            if (endpoint) {
                if (vm.useBrkObjectExpanded) {
                    endpoint = endpoint.replace('brk/object', 'brk/object-expand');
                }

                vm.apiData = {};

                //Load the first page
                loadData(endpoint);

                //Load pages 2-n
                vm.loadMore = function () {
                    loadData(vm.apiData.next);
                };
            }
        });

        function loadData (endpoint) {
            api.getByUrl(endpoint).then(function (response) {
                var hasPagination = angular.isArray(response.results);

                if (hasPagination) {
                    if (angular.isUndefined(vm.apiData.results)) {
                        vm.apiData.results = [];
                    }

                    vm.apiData.count = response.count;
                    vm.apiData.results = vm.apiData.results.concat(response.results);
                    vm.apiData.next = response._links.next && response._links.next.href;
                } else {
                    vm.apiData.results = response;
                }
            });
        }
    }
})();
<|endoftext|>


<commit_before>import _ from 'lodash'

const VALID_BEHAVIORS = ['disallow', 'allow', 'allow-then-deactivate']
const DEFAULT_POLICY = {_: 'allow'}

const assertFallbackPolicy = activatable => {
    if (activatable.policy && !activatable.policy._) {
        throw Error(`Policy '${activatable.id}' is missing fallback behavior: ${activatable.policy}`)
    }
}

export const activationAllowed = (id, activatables = {}) => {
    _(activatables)
        .values()
        .forEach(activatable => assertFallbackPolicy(activatable))

    const [[thisActivatable], otherActivatables] =
        _(activatables)
            .mapValues((activatable, id) => ({...activatable, id}))
            .partition(activatable => activatable.id === id)
            .value()

    return thisActivatable && !thisActivatable.active
        ? thisActivatable.alwaysAllow || _(otherActivatables)
            .pickBy(otherActivatable => otherActivatable.active)
            .every(otherActivatable => policiesCompatible(thisActivatable, otherActivatable))
        : false
}

const getPolicy = activatable => activatable.policy || DEFAULT_POLICY

const policiesCompatible = (thisActivatable, otherActivatable) => {
    const thisPolicy = getPolicy(thisActivatable)
    const otherPolicy = getPolicy(otherActivatable)
    const thisCompatibleWithOther = compatibleWith(thisActivatable.id, otherPolicy)
    const otherCompatibleWithThis = compatibleWith(otherActivatable.id, thisPolicy)
    const otherShouldDeactivate = deactivateWhen(thisActivatable.id, otherPolicy)
    const thisShouldDeactivate = deactivateWhen(otherActivatable.id, thisPolicy)
    return thisCompatibleWithOther
        && (otherShouldDeactivate || (otherCompatibleWithThis && !thisShouldDeactivate))
}

const validateBehavior = behavior => {
    if (!VALID_BEHAVIORS.includes(behavior)) {
        throw Error(`Invalid policy behavior: ${behavior}`)
    }
    return behavior
}

const behavior = (id, policy) =>
    validateBehavior(policy[id] || policy._)

const compatibleWith = (id, policy) =>
    behavior(id, policy) !== 'disallow'

const deactivateWhen = (id, policy) =>
    behavior(id, policy) === 'allow-then-deactivate'

export const shouldDeactivate = (id, activatables = {}, nextPolicy) => {
    const alwaysAllow = id => activatables[id].alwaysAllow
    const isDeactivatable = activatableId => {
        const active = activatables[activatableId].active
        const preventedDeactivation = alwaysAllow(activatableId) && !alwaysAllow(id)
        return active && !preventedDeactivation
    }
    return _(activatables)
        .keys()
        .filter(activeId => activeId !== id)
        .filter(activatableId => isDeactivatable(activatableId))
        .some(activeId => deactivateWhen(activeId, nextPolicy))
}
<commit_msg>Made activatables compatible if one any is alwaysAllow.<commit_after>import _ from 'lodash'

const VALID_BEHAVIORS = ['disallow', 'allow', 'allow-then-deactivate']
const DEFAULT_POLICY = {_: 'allow'}

const assertFallbackPolicy = activatable => {
    if (activatable.policy && !activatable.policy._) {
        throw Error(`Policy '${activatable.id}' is missing fallback behavior: ${activatable.policy}`)
    }
}

export const activationAllowed = (id, activatables = {}) => {
    _(activatables)
        .values()
        .forEach(activatable => assertFallbackPolicy(activatable))

    const [[thisActivatable], otherActivatables] =
        _(activatables)
            .mapValues((activatable, id) => ({...activatable, id}))
            .partition(activatable => activatable.id === id)
            .value()

    return thisActivatable && !thisActivatable.active
        ? thisActivatable.alwaysAllow || _(otherActivatables)
            .pickBy(otherActivatable => otherActivatable.active)
            .every(otherActivatable => policiesCompatible(thisActivatable, otherActivatable))
        : false
}

const getPolicy = activatable => activatable.policy || DEFAULT_POLICY

const policiesCompatible = (thisActivatable, otherActivatable) => {
    const thisPolicy = getPolicy(thisActivatable)
    const otherPolicy = getPolicy(otherActivatable)
    const thisCompatibleWithOther = compatibleWith(thisActivatable.id, otherPolicy)
    const otherCompatibleWithThis = compatibleWith(otherActivatable.id, thisPolicy)
    const otherShouldDeactivate = deactivateWhen(thisActivatable.id, otherPolicy)
    const thisShouldDeactivate = deactivateWhen(otherActivatable.id, thisPolicy)
    return (thisActivatable.alwaysAllow || otherActivatable.alwaysAllow) ||
        (thisCompatibleWithOther && (otherShouldDeactivate || (otherCompatibleWithThis && !thisShouldDeactivate)))
}

const validateBehavior = behavior => {
    if (!VALID_BEHAVIORS.includes(behavior)) {
        throw Error(`Invalid policy behavior: ${behavior}`)
    }
    return behavior
}

const behavior = (id, policy) =>
    validateBehavior(policy[id] || policy._)

const compatibleWith = (id, policy) =>
    behavior(id, policy) !== 'disallow'

const deactivateWhen = (id, policy) =>
    behavior(id, policy) === 'allow-then-deactivate'

export const shouldDeactivate = (id, activatables = {}, nextPolicy) => {
    const alwaysAllow = id => activatables[id].alwaysAllow
    const isDeactivatable = activatableId => {
        const active = activatables[activatableId].active
        const preventedDeactivation = alwaysAllow(activatableId) && !alwaysAllow(id)
        return active && !preventedDeactivation
    }
    return _(activatables)
        .keys()
        .filter(activeId => activeId !== id)
        .filter(activatableId => isDeactivatable(activatableId))
        .some(activeId => deactivateWhen(activeId, nextPolicy))
}
<|endoftext|>


<commit_before>'use strict';

var page = require('./page'),
    router = require('routes')();

router.addRoute('/product/:id', ['product', page.product]);
router.addRoute('/main', ['top', page.main]);

// The single routing action can be used at both server/client side.
module.exports = function () {
    var path = this.getStore('page')._get('url').pathname,
        match = router.match(path);

    if (!match) {
        return Promise.reject(new Error('no matched route'));
    }

    this.dispatch('UPDATE_ROUTING', {
        name: match.fn[0],
        params: match.params
    });

    return this.executeAction(match.fn[1]);
};
<commit_msg>refine route error msg<commit_after>'use strict';

var page = require('./page'),
    router = require('routes')();

router.addRoute('/product/:id', ['product', page.product]);
router.addRoute('/main', ['top', page.main]);

// The single routing action can be used at both server/client side.
module.exports = function () {
    var path = this.getStore('page')._get('url').pathname,
        match = router.match(path);

    if (!match) {
        return Promise.reject(new Error('no matched route for url: ' + path));
    }

    this.dispatch('UPDATE_ROUTING', {
        name: match.fn[0],
        params: match.params
    });

    return this.executeAction(match.fn[1]);
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

/* eslint-disable no-param-reassign */

import { ContentState, ContentBlock, CharacterMetadata, genKey } from 'draft-js';
import Entity from 'draft-js/lib/DraftEntity';
import getSafeBodyFromHTML from 'draft-js/lib/getSafeBodyFromHTML';
import sanitizeDraftText from 'draft-js/lib/sanitizeDraftText';
import { List, OrderedSet, Map } from 'immutable';
import nullthrows from 'nullthrows';

import type { DraftInlineStyle } from 'draft-js/lib/DraftInlineStyle';

import getEmptyChunk from './getEmptyChunk';
import getWhitespaceChunk from './getWhitespaceChunk';
import getSoftNewlineChunk from './getSoftNewlineChunk';
import getBlockDividerChunk from './getBlockDividerChunk';
import getBlockTypeForTag from './getBlockTypeForTag';
import processInlineTag from './processInlineTag';
import joinChunks from './joinChunks';
import getChunkWithEntity from './getChunkWithEntity';
import normalizeHTML from './normalizeHTML';
import containsSemanticBlockMarkup from './helpers/containsSemanticBlockMarkup';
import shouldCreateLinkEntity from './helpers/shouldCreateLinkEntity';
import { SPACE, CR, REGEX_LF, BASE_BLOCK_TAG_NAMES } from './constants';
import { BLOCK_TYPES, ENTITY_TYPES } from '../../constants';

import type { Chunk } from './types';

let lastBlock;

const genFragment = (
  node: Node,
  inlineStyle: DraftInlineStyle,
  lastList: string,
  inBlock: ?string,
  blockTags: Array<string>,
  depth: number,
  inEntity?: string
): Chunk => {
  let nodeName = node.nodeName.toLowerCase();
  let newBlock = false;
  let nextBlockType = BLOCK_TYPES.UNSTYLED;
  const lastLastBlock = lastBlock;

  // Base Case
  if (nodeName === '#text') {
    let text = node.textContent;
    if (text.trim() === '' && inBlock !== 'pre') {
      return getWhitespaceChunk(inEntity);
    }
    if (inBlock !== 'pre') {
      // Can't use empty string because MSWord
      text = text.replace(REGEX_LF, SPACE);
    }

    // save the last block so we can use it later
    lastBlock = nodeName;

    return {
      text,
      inlines: Array(text.length).fill(inlineStyle),
      entities: Array(text.length).fill(inEntity),
      blocks: [],
    };
  }

  const chunkWithEntity = getChunkWithEntity(
    nodeName,
    node,
    inlineStyle,
    depth
  );

  if (chunkWithEntity) {
    return chunkWithEntity;
  }

  // save the last block so we can use it later
  lastBlock = nodeName;

  // BR tags
  if (nodeName === 'br') {
    if (
        lastLastBlock === 'br' &&
        (!inBlock || getBlockTypeForTag(inBlock, lastList, node) === BLOCK_TYPES.UNSTYLED)
    ) {
      return getBlockDividerChunk(BLOCK_TYPES.UNSTYLED, depth);
    }
    return getSoftNewlineChunk();
  }

  let chunk = getEmptyChunk();
  let newChunk: ?Chunk = null;

  // Inline tags
  inlineStyle = processInlineTag(nodeName, node, inlineStyle);

  // Handle lists
  if (nodeName === 'ul' || nodeName === 'ol') {
    if (lastList) {
      depth += 1;
    }
    lastList = nodeName;
  }

  // Block Tags
  if (!inBlock && blockTags.indexOf(nodeName) !== -1) {
    chunk = getBlockDividerChunk(
      getBlockTypeForTag(nodeName, lastList, node),
      depth,
      node
    );
    inBlock = nodeName;
    newBlock = true;
  } else if (lastList && inBlock === 'li' && nodeName === 'li') {
    chunk = getBlockDividerChunk(
      getBlockTypeForTag(nodeName, lastList, node),
      depth,
      node
    );
    inBlock = nodeName;
    newBlock = true;
    if (lastList === 'ul') {
      if (node.classList.contains('task-list-item')) {
        nextBlockType = BLOCK_TYPES.CHECKABLE_LIST_ITEM;
      } else {
        nextBlockType = BLOCK_TYPES.UNORDERED_LIST_ITEM;
      }
    } else {
      nextBlockType = BLOCK_TYPES.ORDERED_LIST_ITEM;
    }
  }

  // Recurse through children
  let child: ?Node = node.firstChild;
  if (child != null) {
    nodeName = child.nodeName.toLowerCase();
  }

  let entityId: ?string = null;
  let href: ?string = null;

  while (child) {
    if (shouldCreateLinkEntity(nodeName, child)) {
      // href = new URI(child.href).toString();
      href = child.href;
      if (child.hasAttribute('download')) {
        // TODO: update this when DraftEntity removed  entirely
        entityId = Entity.__create(ENTITY_TYPES.DOWNLOAD_LINK, 'MUTABLE', {
          url: href,
          name: child.getAttribute('data-name'),
          size: child.getAttribute('data-size')
        });
      } else {
        // TODO: update this when DraftEntity removed  entirely
        entityId = Entity.__create(ENTITY_TYPES.LINK, 'MUTABLE', { url: href });
      }
    } else {
      entityId = undefined;
    }

    newChunk = genFragment(
      child,
      inlineStyle,
      lastList,
      inBlock,
      blockTags,
      depth,
      entityId || inEntity
    );

    chunk = joinChunks(chunk, newChunk);
    const sibling: Node = child.nextSibling;

    // Put in a newline to break up blocks inside blocks
    if (
      sibling &&
      blockTags.indexOf(nodeName) >= 0 &&
      inBlock
    ) {
      chunk = joinChunks(chunk, getSoftNewlineChunk());
    }
    if (sibling) {
      nodeName = sibling.nodeName.toLowerCase();
    }
    child = sibling;
  }

  if (newBlock) {
    chunk = joinChunks(
      chunk,
      getBlockDividerChunk(nextBlockType, depth)
    );
  }

  return chunk;
};

const getChunkForHTML = (html: string, DOMBuilder: Function): ?Chunk => {
  html = normalizeHTML(html);

  const safeBody = DOMBuilder(html);
  if (!safeBody) {
    return null;
  }
  lastBlock = null;

  // Sometimes we aren't dealing with content that contains nice semantic
  // tags. In this case, use divs to separate everything out into paragraphs
  // and hope for the best.
  const workingBlocks = containsSemanticBlockMarkup(html) ? BASE_BLOCK_TAG_NAMES : ['div'];

  // Start with -1 block depth to offset the fact that we are passing in a fake
  // UL block to start with.
  let chunk =
    genFragment(safeBody, OrderedSet(), 'ul', null, workingBlocks, -1);

  // join with previous block to prevent weirdness on paste
  if (chunk.text.indexOf(CR) === 0) {
    chunk = {
      text: chunk.text.slice(1),
      inlines: chunk.inlines.slice(1),
      entities: chunk.entities.slice(1),
      blocks: chunk.blocks,
    };
  }

  // Kill block delimiter at the end
  if (chunk.text.slice(-1) === CR) {
    chunk.text = chunk.text.slice(0, -1);
    chunk.inlines = chunk.inlines.slice(0, -1);
    chunk.entities = chunk.entities.slice(0, -1);
    chunk.blocks.pop();
  }

  // If we saw no block tags, put an unstyled one in
  if (chunk.blocks.length === 0) {
    chunk.blocks.push({ type: BLOCK_TYPES.UNSTYLED, depth: 0 });
  }

  // Sometimes we start with text that isn't in a block, which is then
  // followed by blocks. Need to fix up the blocks to add in
  // an unstyled block for this content
  if (chunk.text.split(CR).length === chunk.blocks.length + 1) {
    chunk.blocks.unshift({ type: BLOCK_TYPES.UNSTYLED, depth: 0 });
  }

  return chunk;
};

const convertHTMLToContentState = (
  html: string,
  DOMBuilder: Function = getSafeBodyFromHTML
): ContentState => {
  // Be ABSOLUTELY SURE that the dom builder you pass hare won't execute
  // arbitrary code in whatever environment you're running this in. For an
  // example of how we try to do this in-browser, see getSafeBodyFromHTML.

  const chunk = getChunkForHTML(html, DOMBuilder);
  if (chunk == null) {
    return null;
  }
  let start = 0;

  const blockArray = chunk.text.split(CR).map(
    (textBlock, ii) => {
      // Make absolutely certain that our text is acceptable.
      textBlock = sanitizeDraftText(textBlock);
      const end = start + textBlock.length;
      const inlines = nullthrows(chunk).inlines.slice(start, end);
      const entities = nullthrows(chunk).entities.slice(start, end);
      const characterList = List(
        inlines.map((style, jj) => {
          const data = { style, entity: (null: ?string) };
          if (entities[jj]) {
            data.entity = entities[jj];
          }
          return CharacterMetadata.create(data);
        })
      );
      start = end + 1;

      return new ContentBlock({
        key: genKey(),
        type: nullthrows(chunk).blocks[ii].type,
        depth: nullthrows(chunk).blocks[ii].depth,
        data: nullthrows(chunk).blocks[ii].data || Map(),
        text: textBlock,
        characterList,
      });
    }
  );

  return ContentState.createFromBlockArray(blockArray);
};

export default convertHTMLToContentState;
<commit_msg>Add textToEntity option to convertHTMLToContent<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

/* eslint-disable no-param-reassign */

import { ContentState, ContentBlock, CharacterMetadata, genKey } from 'draft-js';
import Entity from 'draft-js/lib/DraftEntity';
import getSafeBodyFromHTML from 'draft-js/lib/getSafeBodyFromHTML';
import sanitizeDraftText from 'draft-js/lib/sanitizeDraftText';
import { List, OrderedSet, Map } from 'immutable';
import nullthrows from 'nullthrows';

import type { DraftInlineStyle } from 'draft-js/lib/DraftInlineStyle';

import getEmptyChunk from './getEmptyChunk';
import getWhitespaceChunk from './getWhitespaceChunk';
import getSoftNewlineChunk from './getSoftNewlineChunk';
import getBlockDividerChunk from './getBlockDividerChunk';
import getBlockTypeForTag from './getBlockTypeForTag';
import processInlineTag from './processInlineTag';
import joinChunks from './joinChunks';
import getChunkWithEntity from './getChunkWithEntity';
import normalizeHTML from './normalizeHTML';
import containsSemanticBlockMarkup from './helpers/containsSemanticBlockMarkup';
import shouldCreateLinkEntity from './helpers/shouldCreateLinkEntity';
import { SPACE, CR, REGEX_LF, BASE_BLOCK_TAG_NAMES } from './constants';
import { BLOCK_TYPES, ENTITY_TYPES } from '../../constants';

import type { Chunk } from './types';

let lastBlock;

const genFragment = (
  node: Node,
  inlineStyle: DraftInlineStyle,
  lastList: string,
  inBlock: ?string,
  blockTags: Array<string>,
  depth: number,
  inEntity?: string,
  options: Object
): Chunk => {
  let nodeName = node.nodeName.toLowerCase();
  let newBlock = false;
  let nextBlockType = BLOCK_TYPES.UNSTYLED;
  const lastLastBlock = lastBlock;

  // Base Case
  if (nodeName === '#text') {
    let text = node.textContent;
    if (text.trim() === '' && inBlock !== 'pre') {
      return getWhitespaceChunk(inEntity);
    }
    if (inBlock !== 'pre') {
      // Can't use empty string because MSWord
      text = text.replace(REGEX_LF, SPACE);
    }

    // save the last block so we can use it later
    lastBlock = nodeName;

    const entities = Array(text.length).fill(inEntity);

    let offsetChange = 0;
    const textEntities = options.textToEntity(text).sort((r1, r2) => {
      if (r1.offset === r2.offset) {
        return r2.length - r1.length;
      }
      return r1.offset - r2.offset;
    });
    textEntities.forEach(({ entity, offset, length, result }) => {
      const adjustedOffset = offset + offsetChange;

      if (result === null || result === undefined) {
        result = text.substr(adjustedOffset, length);
      }

      const textArray = text.split('');
      textArray.splice.bind(textArray, adjustedOffset, length).apply(textArray, result.split(''));
      text = textArray.join('');

      entities.splice
        .bind(entities, adjustedOffset, length)
        .apply(entities, Array(result.length).fill(entity));

      offsetChange += result.length - length;
    });

    return {
      text,
      inlines: Array(text.length).fill(inlineStyle),
      entities,
      blocks: [],
    };
  }

  const chunkWithEntity = getChunkWithEntity(
    nodeName,
    node,
    inlineStyle,
    depth
  );

  if (chunkWithEntity) {
    return chunkWithEntity;
  }

  // save the last block so we can use it later
  lastBlock = nodeName;

  // BR tags
  if (nodeName === 'br') {
    if (
        lastLastBlock === 'br' &&
        (!inBlock || getBlockTypeForTag(inBlock, lastList, node) === BLOCK_TYPES.UNSTYLED)
    ) {
      return getBlockDividerChunk(BLOCK_TYPES.UNSTYLED, depth);
    }
    return getSoftNewlineChunk();
  }

  let chunk = getEmptyChunk();
  let newChunk: ?Chunk = null;

  // Inline tags
  inlineStyle = processInlineTag(nodeName, node, inlineStyle);

  // Handle lists
  if (nodeName === 'ul' || nodeName === 'ol') {
    if (lastList) {
      depth += 1;
    }
    lastList = nodeName;
  }

  // Block Tags
  if (!inBlock && blockTags.indexOf(nodeName) !== -1) {
    chunk = getBlockDividerChunk(
      getBlockTypeForTag(nodeName, lastList, node),
      depth,
      node
    );
    inBlock = nodeName;
    newBlock = true;
  } else if (lastList && inBlock === 'li' && nodeName === 'li') {
    chunk = getBlockDividerChunk(
      getBlockTypeForTag(nodeName, lastList, node),
      depth,
      node
    );
    inBlock = nodeName;
    newBlock = true;
    if (lastList === 'ul') {
      if (node.classList.contains('task-list-item')) {
        nextBlockType = BLOCK_TYPES.CHECKABLE_LIST_ITEM;
      } else {
        nextBlockType = BLOCK_TYPES.UNORDERED_LIST_ITEM;
      }
    } else {
      nextBlockType = BLOCK_TYPES.ORDERED_LIST_ITEM;
    }
  }

  // Recurse through children
  let child: ?Node = node.firstChild;
  if (child != null) {
    nodeName = child.nodeName.toLowerCase();
  }

  let entityId: ?string = null;
  let href: ?string = null;

  while (child) {
    if (shouldCreateLinkEntity(nodeName, child)) {
      // href = new URI(child.href).toString();
      href = child.href;
      if (child.hasAttribute('download')) {
        // TODO: update this when DraftEntity removed  entirely
        entityId = Entity.__create(ENTITY_TYPES.DOWNLOAD_LINK, 'MUTABLE', {
          url: href,
          name: child.getAttribute('data-name'),
          size: child.getAttribute('data-size')
        });
      } else {
        // TODO: update this when DraftEntity removed  entirely
        entityId = Entity.__create(ENTITY_TYPES.LINK, 'MUTABLE', { url: href });
      }
    } else {
      entityId = undefined;
    }

    newChunk = genFragment(
      child,
      inlineStyle,
      lastList,
      inBlock,
      blockTags,
      depth,
      entityId || inEntity,
      options
    );

    chunk = joinChunks(chunk, newChunk);
    const sibling: Node = child.nextSibling;

    // Put in a newline to break up blocks inside blocks
    if (
      sibling &&
      blockTags.indexOf(nodeName) >= 0 &&
      inBlock
    ) {
      chunk = joinChunks(chunk, getSoftNewlineChunk());
    }
    if (sibling) {
      nodeName = sibling.nodeName.toLowerCase();
    }
    child = sibling;
  }

  if (newBlock) {
    chunk = joinChunks(
      chunk,
      getBlockDividerChunk(nextBlockType, depth)
    );
  }

  return chunk;
};

const getChunkForHTML = (html: string, DOMBuilder: Function, options: Object): ?Chunk => {
  html = normalizeHTML(html);

  const safeBody = DOMBuilder(html);
  if (!safeBody) {
    return null;
  }
  lastBlock = null;

  // Sometimes we aren't dealing with content that contains nice semantic
  // tags. In this case, use divs to separate everything out into paragraphs
  // and hope for the best.
  const workingBlocks = containsSemanticBlockMarkup(html) ? BASE_BLOCK_TAG_NAMES : ['div'];

  // Start with -1 block depth to offset the fact that we are passing in a fake
  // UL block to start with.
  let chunk =
    genFragment(safeBody, OrderedSet(), 'ul', null, workingBlocks, -1, null, options);

  // join with previous block to prevent weirdness on paste
  if (chunk.text.indexOf(CR) === 0) {
    chunk = {
      text: chunk.text.slice(1),
      inlines: chunk.inlines.slice(1),
      entities: chunk.entities.slice(1),
      blocks: chunk.blocks,
    };
  }

  // Kill block delimiter at the end
  if (chunk.text.slice(-1) === CR) {
    chunk.text = chunk.text.slice(0, -1);
    chunk.inlines = chunk.inlines.slice(0, -1);
    chunk.entities = chunk.entities.slice(0, -1);
    chunk.blocks.pop();
  }

  // If we saw no block tags, put an unstyled one in
  if (chunk.blocks.length === 0) {
    chunk.blocks.push({ type: BLOCK_TYPES.UNSTYLED, depth: 0 });
  }

  // Sometimes we start with text that isn't in a block, which is then
  // followed by blocks. Need to fix up the blocks to add in
  // an unstyled block for this content
  if (chunk.text.split(CR).length === chunk.blocks.length + 1) {
    chunk.blocks.unshift({ type: BLOCK_TYPES.UNSTYLED, depth: 0 });
  }

  return chunk;
};

const convertHTMLToContentState = (
  html: string,
  DOMBuilder: Function = getSafeBodyFromHTML,
  options: Object = { textToEntity: () => [] }
): ContentState => {
  // Be ABSOLUTELY SURE that the dom builder you pass hare won't execute
  // arbitrary code in whatever environment you're running this in. For an
  // example of how we try to do this in-browser, see getSafeBodyFromHTML.

  const chunk = getChunkForHTML(html, DOMBuilder, options);
  if (chunk == null) {
    return null;
  }
  let start = 0;

  const blockArray = chunk.text.split(CR).map(
    (textBlock, ii) => {
      // Make absolutely certain that our text is acceptable.
      textBlock = sanitizeDraftText(textBlock);
      const end = start + textBlock.length;
      const inlines = nullthrows(chunk).inlines.slice(start, end);
      const entities = nullthrows(chunk).entities.slice(start, end);
      const characterList = List(
        inlines.map((style, jj) => {
          const data = { style, entity: (null: ?string) };
          if (entities[jj]) {
            data.entity = entities[jj];
          }
          return CharacterMetadata.create(data);
        })
      );
      start = end + 1;

      return new ContentBlock({
        key: genKey(),
        type: nullthrows(chunk).blocks[ii].type,
        depth: nullthrows(chunk).blocks[ii].depth,
        data: nullthrows(chunk).blocks[ii].data || Map(),
        text: textBlock,
        characterList,
      });
    }
  );

  return ContentState.createFromBlockArray(blockArray);
};

export default convertHTMLToContentState;
<|endoftext|>


<commit_before>/**
 * @author zhixin wen <wenzhixin2010@gmail.com>
 * extensions: https://github.com/hhurz/tableExport.jquery.plugin
 */

($ => {
  const Utils = $.fn.bootstrapTable.utils

  const bootstrap = {
    3: {
      icons: {
        export: 'glyphicon-export icon-share'
      },
      html: {
        dropmenu: '

',
        dropitem: '
%s
'
      }
    },
    4: {
      icons: {
        export: 'fa-download'
      },
      html: {
        dropmenu: '

',
        dropitem: '%s'
      }
    }
  }[Utils.bootstrapVersion]

  const TYPE_NAME = {
    json: 'JSON',
    xml: 'XML',
    png: 'PNG',
    csv: 'CSV',
    txt: 'TXT',
    sql: 'SQL',
    doc: 'MS-Word',
    excel: 'MS-Excel',
    xlsx: 'MS-Excel (OpenXML)',
    powerpoint: 'MS-Powerpoint',
    pdf: 'PDF'
  }

  $.extend($.fn.bootstrapTable.defaults, {
    showExport: false,
    exportDataType: 'basic', // basic, all, selected
    exportTypes: ['json', 'xml', 'csv', 'txt', 'sql', 'excel'],
    exportOptions: {},
    exportFooter: false
  })

  $.extend($.fn.bootstrapTable.defaults.icons, {
    export: bootstrap.icons.export
  })

  $.extend($.fn.bootstrapTable.locales, {
    formatExport () {
      return 'Export data'
    }
  })
  $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)

  $.fn.bootstrapTable.methods.push('exportTable')

  $.BootstrapTable = class extends $.BootstrapTable {
    initToolbar () {
      const o = this.options

      this.showToolbar = this.showToolbar || o.showExport

      super.initToolbar()

      if (!this.options.showExport) {
        return
      }
      const $btnGroup = this.$toolbar.find('>.btn-group')
      let $export = $btnGroup.find('div.export')

      if ($export.length) {
        return
      }
      $export = $(`

        <button class="btn btn-${o.buttonsClass} btn-${o.iconSize} dropdown-toggle"
          aria-label="export type"
          title="${o.formatExport()}"
          data-toggle="dropdown"
          type="button">

        </button>
        ${bootstrap.html.dropmenu}

      `).appendTo($btnGroup)

      const $menu = $export.find('.dropdown-menu')
      let exportTypes = o.exportTypes

      if (typeof exportTypes === 'string') {
        const types = exportTypes.slice(1, -1).replace(/ /g, '').split(',')
        exportTypes = types.map(t => t.slice(1, -1))
      }
      for (let type of exportTypes) {
        if (TYPE_NAME.hasOwnProperty(type)) {
          $menu.append(Utils.sprintf(bootstrap.html.dropitem, type, TYPE_NAME[type]))
        }
      }

      $menu.find('>li, >a').click(e => {
        const type = $(e.currentTarget).data('type')
        const exportOptions = {
          type: type,
          escape: false
        }

        this.exportTable(exportOptions)
      })
    }

    exportTable (options) {
      const o = this.options

      const doExport = () => {
        const that = this
        const data = this.getData()
        if (o.exportFooter) {
          const $footerRow = this.$tableFooter.find('tr').first()
          const footerData = {}
          const footerHtml = []

          $.each($footerRow.children(), function (index, footerCell) {
            var footerCellHtml = $(footerCell).children('.th-inner').first().html()
            footerData[that.columns[index].field] = footerCellHtml === ' ' ? null : footerCellHtml

            // grab footer cell text into cell index-based array
            footerHtml.push(footerCellHtml)
          })

          this.append(footerData)

          var $lastTableRow = this.$body.children().last()

          $.each($lastTableRow.children(), function (index, lastTableRowCell) {
            $(lastTableRowCell).html(footerHtml[index])
          })
        }

        this.$el.tableExport($.extend({}, o.exportOptions, options))

        if (o.exportFooter) {
          this.load(data)
        }
      }

      const stateField = this.header.stateField

      if (o.exportDataType === 'all' && o.pagination) {
        const eventName = o.sidePagination === 'server'
          ? 'post-body.bs.table' : 'page-change.bs.table'
        this.$el.one(eventName, () => {
          if (stateField) {
            this.hideColumn(stateField)
          }
          doExport()
          this.togglePagination()
        })
        this.togglePagination()
      } else if (o.exportDataType === 'selected') {
        let data = this.getData()
        let selectedData = this.getSelections()
        if (!selectedData.length) {
          return
        }

        if (o.sidePagination === 'server') {
          data = {
            total: o.totalRows,
            [this.options.dataField]: data
          }
          selectedData = {
            total: selectedData.length,
            [this.options.dataField]: selectedData
          }
        }

        this.load(selectedData)
        if (stateField) {
          this.hideColumn(stateField)
        }
        doExport()
        this.load(data)
      } else {
        if (stateField) {
          this.hideColumn(stateField)
        }
        doExport()
      }
      if (stateField) {
        this.showColumn(stateField)
      }
    }
  }
})(jQuery)
<commit_msg>fix(export extension): fix #4086, export in cardView display error<commit_after>/**
 * @author zhixin wen <wenzhixin2010@gmail.com>
 * extensions: https://github.com/hhurz/tableExport.jquery.plugin
 */

($ => {
  const Utils = $.fn.bootstrapTable.utils

  const bootstrap = {
    3: {
      icons: {
        export: 'glyphicon-export icon-share'
      },
      html: {
        dropmenu: '

',
        dropitem: '
%s
'
      }
    },
    4: {
      icons: {
        export: 'fa-download'
      },
      html: {
        dropmenu: '

',
        dropitem: '%s'
      }
    }
  }[Utils.bootstrapVersion]

  const TYPE_NAME = {
    json: 'JSON',
    xml: 'XML',
    png: 'PNG',
    csv: 'CSV',
    txt: 'TXT',
    sql: 'SQL',
    doc: 'MS-Word',
    excel: 'MS-Excel',
    xlsx: 'MS-Excel (OpenXML)',
    powerpoint: 'MS-Powerpoint',
    pdf: 'PDF'
  }

  $.extend($.fn.bootstrapTable.defaults, {
    showExport: false,
    exportDataType: 'basic', // basic, all, selected
    exportTypes: ['json', 'xml', 'csv', 'txt', 'sql', 'excel'],
    exportOptions: {},
    exportFooter: false
  })

  $.extend($.fn.bootstrapTable.defaults.icons, {
    export: bootstrap.icons.export
  })

  $.extend($.fn.bootstrapTable.locales, {
    formatExport () {
      return 'Export data'
    }
  })
  $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)

  $.fn.bootstrapTable.methods.push('exportTable')

  $.BootstrapTable = class extends $.BootstrapTable {
    initToolbar () {
      const o = this.options

      this.showToolbar = this.showToolbar || o.showExport

      super.initToolbar()

      if (!this.options.showExport) {
        return
      }
      const $btnGroup = this.$toolbar.find('>.btn-group')
      let $export = $btnGroup.find('div.export')

      if ($export.length) {
        return
      }
      $export = $(`

        <button class="btn btn-${o.buttonsClass} btn-${o.iconSize} dropdown-toggle"
          aria-label="export type"
          title="${o.formatExport()}"
          data-toggle="dropdown"
          type="button">

        </button>
        ${bootstrap.html.dropmenu}

      `).appendTo($btnGroup)

      const $menu = $export.find('.dropdown-menu')
      let exportTypes = o.exportTypes

      if (typeof exportTypes === 'string') {
        const types = exportTypes.slice(1, -1).replace(/ /g, '').split(',')
        exportTypes = types.map(t => t.slice(1, -1))
      }
      for (let type of exportTypes) {
        if (TYPE_NAME.hasOwnProperty(type)) {
          $menu.append(Utils.sprintf(bootstrap.html.dropitem, type, TYPE_NAME[type]))
        }
      }

      $menu.find('>li, >a').click(e => {
        const type = $(e.currentTarget).data('type')
        const exportOptions = {
          type: type,
          escape: false
        }

        this.exportTable(exportOptions)
      })
    }

    exportTable (options) {
      const o = this.options

      const doExport = () => {
        const that = this
        const data = this.getData()
        if (o.exportFooter) {
          const $footerRow = this.$tableFooter.find('tr').first()
          const footerData = {}
          const footerHtml = []

          $.each($footerRow.children(), function (index, footerCell) {
            var footerCellHtml = $(footerCell).children('.th-inner').first().html()
            footerData[that.columns[index].field] = footerCellHtml === ' ' ? null : footerCellHtml

            // grab footer cell text into cell index-based array
            footerHtml.push(footerCellHtml)
          })

          this.append(footerData)

          var $lastTableRow = this.$body.children().last()

          $.each($lastTableRow.children(), function (index, lastTableRowCell) {
            $(lastTableRowCell).html(footerHtml[index])
          })
        }

        this.$el.tableExport($.extend({}, o.exportOptions, options))

        if (o.exportFooter) {
          this.load(data)
        }
      }

      const stateField = this.header.stateField
      const isCardView = o.cardView

      if (stateField) {
        this.hideColumn(stateField)
      }
      if (isCardView) {
        this.toggleView()
      }
      if (o.exportDataType === 'all' && o.pagination) {
        const eventName = o.sidePagination === 'server'
          ? 'post-body.bs.table' : 'page-change.bs.table'
        this.$el.one(eventName, () => {
          doExport()
          this.togglePagination()
        })
        this.togglePagination()
      } else if (o.exportDataType === 'selected') {
        let data = this.getData()
        let selectedData = this.getSelections()
        if (!selectedData.length) {
          return
        }

        if (o.sidePagination === 'server') {
          data = {
            total: o.totalRows,
            [this.options.dataField]: data
          }
          selectedData = {
            total: selectedData.length,
            [this.options.dataField]: selectedData
          }
        }

        this.load(selectedData)
        doExport()
        this.load(data)
      } else {
        doExport()
      }
      if (stateField) {
        this.showColumn(stateField)
      }
      if (isCardView) {
        this.toggleView()
      }
    }
  }
})(jQuery)
<|endoftext|>


<commit_before>import { h } from 'preact' /** @jsx h */
import { FormGroup } from '~src/memories/components'

export default ({ input, children, label, meta }) => (
  <FormGroup meta={meta}>

          <select {...input}>
            <option value="">{label}</option>
            {children}
          </select>

  </FormGroup>
)
<commit_msg>fix(components): form group relative import #83<commit_after>import { h } from 'preact' /** @jsx h */
import { FormGroup } from '~src/memories/components/forms'

export default ({ input, children, label, meta }) => (
  <FormGroup meta={meta}>

          <select {...input}>
            <option value="" disabled>{label}</option>
            {children}
          </select>

  </FormGroup>
)
<|endoftext|>


<commit_before>$(document).ready(function(e) {
    // let social know stylebot is installed
    sendAvailabilityMessage();

    // communication channel between stylebot social and the extension
    var $install_divs = $('.stylebot_install_div');

    if ($install_divs.length != 0) {

        // Bind listener for install event
        $install_divs.bind('stylebotInstallEvent', function(e) {
            console.log('Stylebot: Install event received. Installing style...');

            var $post = $(e.target).closest('.post');

            var url = $.trim($post.find('.post_site').text());

            // first, let's check if a style already exists for the url
            stylebot.chrome.doesStyleExist(url, function(response) {

                // if yes, warn the user
                if (response) {
                    console.log("Style for '" + url + "' already exists.");

                    var customEvent = document.createEvent('Event');
                    customEvent.initEvent('stylebotStyleExistsEvent', true, true);
                    e.target.dispatchEvent(customEvent);
                }

                // else save the style
                else
                    saveStyleFromSocial(e);
            });
        });

        // Bind listener for overwrite installation (without checking if style already exists)

        $install_divs.bind('stylebotOverwriteEvent', function(e) {
            console.log('Stylebot: Overwrite event received. Installing style...');

            var $post = $(e.target).closest('.post');

            saveStyleFromSocial(e);
        });
    }
});

// Sends stylebot social an availability message
function sendAvailabilityMessage() {
    // get the first communication DIV in DOM
    var install_div = $('.stylebot_install_div').get(0);

    // dispatch the message
    if (install_div) {
        var customEvent = document.createEvent('Event');
        customEvent.initEvent('stylebotIsAvailableEvent', true, true);
        install_div.dispatchEvent(customEvent);
    }
}

// Send request to background.html to save style along with metadata (id, timestamp, etc.)
function saveStyleFromSocial(installationEvent) {
    // @deprecated
    //
    // var $channel = $(channel);
    // var $post = $channel.closest('.post');
    //
    // var css = $channel.text();
    //
    // var url = $.trim( $post.find('.post_site').text() );
    //
    // var timestamp = $post.attr('data-timestamp');
    //
    // var id = $post.attr('id').substring(4);

    // @new stuff
    var channel = installationEvent.target;
    var $channel = $(channel);
    var data = $channel.data();

    var parser = new CSSParser();

    try {
        var sheet = parser.parse($channel.text(), false, true);
        var rules = CSSUtils.getRulesFromParserObject(sheet);

        // add the meta header
        var header = '\
/**\n\
    Title       :
'
 + data.title + '\n';

        if (data.description) {
            header += '\
    Description :
'
 + data.description + '\n';
        }

        header += '\
    URL         : http://stylebot.me/styles/
'
 + data.id + '\n\
    Author      :
'
 + data.author + '\n\
    Author URL  :
'
 + 'http://stylebot.me/users/' + data.author + '\n\
**/\n
'
;

        var rulesWithMeta = { 'comment-#0' : { comment: header } };

        for (selector in rules)
            rulesWithMeta[selector] = rules[selector];

        stylebot.chrome.save(data.url, rulesWithMeta, { id: data.id, timestamp: data.timestamp });
        stylebot.chrome.pushStyles();

        // send back success message
        var customEvent = document.createEvent('Event');
        customEvent.initEvent('stylebotInstallationSuccessfulEvent', true, true);
        channel.dispatchEvent(customEvent);
    }

    catch (e) {
        console.log('Error parsing css: ' + e);

        // send back error message
        var customEvent = document.createEvent('Event');
        customEvent.initEvent('stylebotInstallationErrorEvent', true, true);
        channel.dispatchEvent(customEvent);
    }
}
<commit_msg>Tweaked the style comment header generated during installation<commit_after>$(document).ready(function(e) {
    // let social know stylebot is installed
    sendAvailabilityMessage();

    // communication channel between stylebot social and the extension
    var $install_divs = $('.stylebot_install_div');

    if ($install_divs.length != 0) {

        // Bind listener for install event
        $install_divs.bind('stylebotInstallEvent', function(e) {
            console.log('Stylebot: Install event received. Installing style...');

            var $post = $(e.target).closest('.post');

            var url = $.trim($post.find('.post_site').text());

            // first, let's check if a style already exists for the url
            stylebot.chrome.doesStyleExist(url, function(response) {

                // if yes, warn the user
                if (response) {
                    console.log("Style for '" + url + "' already exists.");

                    var customEvent = document.createEvent('Event');
                    customEvent.initEvent('stylebotStyleExistsEvent', true, true);
                    e.target.dispatchEvent(customEvent);
                }

                // else save the style
                else
                    saveStyleFromSocial(e);
            });
        });

        // Bind listener for overwrite installation (without checking if style already exists)

        $install_divs.bind('stylebotOverwriteEvent', function(e) {
            console.log('Stylebot: Overwrite event received. Installing style...');

            var $post = $(e.target).closest('.post');

            saveStyleFromSocial(e);
        });
    }
});

// Sends stylebot social an availability message
function sendAvailabilityMessage() {
    // get the first communication DIV in DOM
    var install_div = $('.stylebot_install_div').get(0);

    // dispatch the message
    if (install_div) {
        var customEvent = document.createEvent('Event');
        customEvent.initEvent('stylebotIsAvailableEvent', true, true);
        install_div.dispatchEvent(customEvent);
    }
}

// Send request to background.html to save style along with metadata (id, timestamp, etc.)
function saveStyleFromSocial(installationEvent) {
    // @deprecated
    //
    // var $channel = $(channel);
    // var $post = $channel.closest('.post');
    //
    // var css = $channel.text();
    //
    // var url = $.trim( $post.find('.post_site').text() );
    //
    // var timestamp = $post.attr('data-timestamp');
    //
    // var id = $post.attr('id').substring(4);

    // @new stuff
    var channel = installationEvent.target;
    var $channel = $(channel);
    var data = $channel.data();

    var parser = new CSSParser();

    try {
        var sheet = parser.parse($channel.text(), false, true);
        var rules = CSSUtils.getRulesFromParserObject(sheet);

        // add the meta header
        var header = '/**\n    Title: ' + data.title
        + '\n    URL: http://stylebot.me/styles/' + data.id
        + '\n    Author: ' + data.author
        + '\n    Author URL: http://stylebot.me/users/' + data.author;

        if (data.description) {
            header += '\n    Description: ' + data.description;
        }

        header += '\n**/';

        var rulesWithMeta = { 'comment-#0' : { comment: header } };

        for (selector in rules)
            rulesWithMeta[selector] = rules[selector];

        stylebot.chrome.save(data.url, rulesWithMeta, { id: data.id, timestamp: data.timestamp });
        stylebot.chrome.pushStyles();

        // send back success message
        var customEvent = document.createEvent('Event');
        customEvent.initEvent('stylebotInstallationSuccessfulEvent', true, true);
        channel.dispatchEvent(customEvent);
    }

    catch (e) {
        console.log('Error parsing css: ' + e);

        // send back error message
        var customEvent = document.createEvent('Event');
        customEvent.initEvent('stylebotInstallationErrorEvent', true, true);
        channel.dispatchEvent(customEvent);
    }
}
<|endoftext|>


<commit_before>angular.module('umbraco.services')
.factory('searchService', function ($q, $log, entityResource, contentResource) {
	var m = {results: []};
	var service = {
		results: m,

		searchMembers: function(args){
			entityResource.search(args.term, "Member").then(function(data){

				_.each(data, function(el){
					el.menuUrl = "UmbracoTrees/MemberTree/GetMenu?id=" + el.id + "&application=member";
					el.metaData = {treeAlias: "member"};
					el.title = el.Fields.nodeName;
					el.subTitle = el.Fields.email;
				});

				args.results.push({
					icon: "icon-user",
					editor: "member/member/edit/",
					matches: data
				});
			});
		},
		searchContent: function(args){
			entityResource.search(args.term, "Document").then(function(data){

				_.each(data, function(el){
					el.menuUrl = "UmbracoTrees/ContentTree/GetMenu?id=" + el.id + "&application=content";
					el.metaData = {treeAlias: "content"};
					el.title = el.Fields.nodeName;

					contentResource.getNiceUrl(el.Id).then(function(url){
						el.subTitle = url;
					});
				});

				args.results.push({
					icon: "icon-document",
					editor: "content/content/edit/",
					matches: data
				});
			});
		},
		searchMedia: function(args){
			entityResource.search(args.term, "Media").then(function(data){

				_.each(data, function(el){
					el.menuUrl = "UmbracoTrees/MediaTree/GetMenu?id=" + el.id + "&application=media";
					el.metaData = {treeAlias: "media"};
					el.title = el.Fields.nodeName;
				});

				args.results.push({
					icon: "icon-picture",
					editor: "media/media/edit/",
					matches: data
				});
			});
		},
		search: function(term){
			m.results.length = 0;

			service.searchMedia({term:term, results:m.results});
			service.searchContent({term:term, results:m.results});
			service.searchMembers({term:term, results:m.results});
		},

		setCurrent: function(sectionAlias){
			currentSection = sectionAlias;
		}
	};

	return service;
});<commit_msg>fixes search menu urls<commit_after>angular.module('umbraco.services')
.factory('searchService', function ($q, $log, entityResource, contentResource) {
	var m = {results: []};
	var service = {
		results: m,

		searchMembers: function(args){
			entityResource.search(args.term, "Member").then(function(data){

				_.each(data, function(el){
					el.menuUrl = "UmbracoTrees/MemberTree/GetMenu?id=" + el.Id + "&application=member";
					el.metaData = {treeAlias: "member"};
					el.title = el.Fields.nodeName;
					el.subTitle = el.Fields.email;
				});

				args.results.push({
					icon: "icon-user",
					editor: "member/member/edit/",
					matches: data
				});
			});
		},
		searchContent: function(args){
			entityResource.search(args.term, "Document").then(function(data){

				_.each(data, function(el){
					el.menuUrl = "UmbracoTrees/ContentTree/GetMenu?id=" + el.Id + "&application=content";
					el.metaData = {treeAlias: "content"};
					el.title = el.Fields.nodeName;

					contentResource.getNiceUrl(el.Id).then(function(url){
						el.subTitle = angular.fromJson(url);
					});
				});

				args.results.push({
					icon: "icon-document",
					editor: "content/content/edit/",
					matches: data
				});
			});
		},
		searchMedia: function(args){
			entityResource.search(args.term, "Media").then(function(data){

				_.each(data, function(el){
					el.menuUrl = "UmbracoTrees/MediaTree/GetMenu?id=" + el.Id + "&application=media";
					el.metaData = {treeAlias: "media"};
					el.title = el.Fields.nodeName;
				});

				args.results.push({
					icon: "icon-picture",
					editor: "media/media/edit/",
					matches: data
				});
			});
		},
		search: function(term){
			m.results.length = 0;

			service.searchMedia({term:term, results:m.results});
			service.searchContent({term:term, results:m.results});
			service.searchMembers({term:term, results:m.results});
		},

		setCurrent: function(sectionAlias){
			currentSection = sectionAlias;
		}
	};

	return service;
});<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var sortBy = require('../lib/sortBy');

module.exports = function(dust) {

  /*
  * @description A filter that nests package rates under theatre sections - Needs to be moved
  * @param {object} packageRates Package Rates
  * @param {object} venueProducts venue products
  * @param {object} ticketRates ticket rates
  * @param {object} roomRates room rates
  * @example {@_filterSeats packageRates=packageRatesReply.packageRates venueProducts=venueProductsReply.venueProducts roomRates=packageRatesReply.linked.roomRates ticketRates=packageRatesReply.linked.ticketRates} {/_filterSeats} output loop of sections with seats inside sorted by price
  */

  dust.helpers._filterSeats = function(chunk, context, bodies, params) {
    params = params || {};

    var packageRates = params.packageRates;
    var venueProduct = _.head(params.venueProducts);
    var ticketRates = params.ticketRates;
    var roomRates = params.roomRates;
    var bandColours = params.bandColours;
    var sortByInput = params.sortBy;

    var cheapestRoom = {
      id: 0,
      price: -1
    };

    var reply = {};

    function stubReply() {
      // Stub reply for rates filling in
      // Make new node for every section a theatre has.
      _.forEach(venueProduct.sections, function(section, i) {
        var item = {
          name: section,
          rates: []
        };
        reply[i] = item;
      });
    }

    stubReply();

    // Sort packageRates by Price Ascending.
    var sortedPackageRates = sortBy(packageRates, 'grossPrice');

    function findCheapestRoom() {
      // Find cheapest room rate ID
      _.forEach(roomRates, function(roomRate) {
        if (roomRate.grossPrice < cheapestRoom.price || cheapestRoom.price === -1) {
          cheapestRoom = {
            id: roomRate.id,
            price: roomRate.grossPrice
          };
        }
      });
    }

    // use Transformer show config to add Gold, Silver or Bronze to packageRate
    function assignColoursToBands(packageRate) {
      // assign gold, silver, bronze to packages depending on their current priceBand
      _.forEach(bandColours, function(sectionValue, sectionKey) {
        // check for match between transformer and existing data
        // check for section that might not have full title ( e.g. Grand Circle in Grand Circle (Left) )
        if (ticketRates[packageRate.ids].section === sectionKey || ticketRates[packageRate.ids].section.indexOf( sectionKey ) > -1 ) {
          // match transformer config to existing seat section
          _.forEach(sectionValue, function(bandValue, bandKey) {
            if (ticketRates[packageRate.ids].priceBand === bandKey) {
              // match transformer config to existing priceBand
              ticketRates[packageRate.ids].colour = bandValue;
              packageRate.colour = bandValue;
            }
          });
        }
      });
    }

    function loopReplySections(packageRate) {
      // For every section in the stubbed out reply
      // check if the current package rate is in the current section
      _.forEach(reply, function(item, i) {
        // If so then we want to add this packageRate under the theatre section.
        if (ticketRates[packageRate.links.ticketRates.ids].section === item.name) {
          // WEB-8081
          if (bandColours !== null) {
            assignColoursToBands(packageRate.links.ticketRates);
          }
          reply[i].rates.push(packageRate);
        }
      });
    }

    function loopPackageRatesAndEqualsCheapest() {
      // Loop through the sorted PackageRates
      _.forEach(sortedPackageRates, function(packageRate) {
        // If it has the cheapest room rate keep it, otherwise do nothing.
        if (_.head(packageRate.links.roomRates.ids) === cheapestRoom.id) {
          loopReplySections(packageRate);
        }
      });
    }

    function loopAndBuildHelperOutput() {
      // Loop through finalised reply and add each theatre section to context for looping in template
      _.forEach(reply, function(item) {
        chunk = chunk.render(bodies.block, context.push(item));
      });
    }

    function reorderRates(rates, replyIndex) {
      var bandTypes = {
        'gold': [],
        'silver': [],
        'bronze': []
      };
      var newRates = [];

      // 1 - Push each rate to its own colour array (bandTypes)
      _.forEach(rates, function(rate) {
        var quality = rate.links.ticketRates.colour;
        if (!quality) {
          return;
        }
        bandTypes[quality].push(rate);
      });
      // 2 - Order each bandTypes array by price and from each ordered array
      // pick the cheapest rate and push it into the newRates array
      _.forEach(bandTypes, function(bandType, key) {
        bandType = _.orderBy(bandType, function(rate) {
          return rate.grossPrice;
        }, ['asc']);

        if (bandType.length) {
          bandTypes[key] = bandType;
          newRates.push(bandType[0]);
        }
      });
      // 3 - Order the cheapest rates selected in the step before by price
      newRates = _.orderBy(newRates, function(newRate) {
        return newRate.grossPrice;
      }, ['asc']);

      reply[replyIndex].rates = newRates;
    }
    // For each reply we want to order the inner 'rates' array by price
    // and then we want to reorder the replies themselves always by price
    // taking in consideration just the cheapest rate of each
    function reorderReplies() {
      _.forEach(reply, function(section, i) {
        reorderRates(section.rates, i);
      });
    }

    function getMergedName(currSectionName, nextSectionName) {
      var _name1 = currSectionName
        .toUpperCase()
        .replace(' ', '')
        .replace('(LEFT)', '')
        .replace('(RIGHT)', '');

      var _name2 = nextSectionName
        .toUpperCase()
        .replace(' ', '')
        .replace('(LEFT)', '')
        .replace('(RIGHT)', '');

      if (_name1 !== _name2) {
        return false;
      }

      return currSectionName
        .replace('(Left)', '')
        .replace('(Right)', '')
        .trim();
    }

    function getMergedSection(currSection, nextSection) {
      var mergedReply = currSection;
      var newName = getMergedName(currSection.name, nextSection.name);
      if (!newName) {
        return false;
      }

      var mixedRates = currSection.rates.concat(nextSection.rates);
      var newRatesObj = {};

      for (var i = 0; i < mixedRates.length; i++) {
        var currRate = mixedRates[i];
        var currColour = currRate.links.ticketRates.colour;

        var condition = !newRatesObj[currColour]
            || (currRate.grossPrice < newRatesObj[currColour].grossPrice)
            || ((currRate.grossPrice === newRatesObj[currColour].grossPrice) && Math.random() >= 0.5);

        if (condition) {
          newRatesObj[currColour] = currRate;
        }
      }

      mergedReply.name = newName;
      mergedReply.rates = _.values(newRatesObj);

      return mergedReply;
    }

    function mergeReplies() {
      var newReply = {};
      var loopMax = _.size(reply);

      for (var i = 0, newReplyIndex = 0; i < loopMax; i++, newReplyIndex++) {
        if (!reply[i + 1]) {
          newReply[newReplyIndex] = reply[i];
          break;
        }

        var mergedSection = getMergedSection(reply[i], reply[i + 1]);

        if (mergedSection) {
          newReply[newReplyIndex] = mergedSection;
          i++;
        } else {
          newReply[newReplyIndex] = reply[i];
        }
      }

      reply = newReply;
    }

    findCheapestRoom();
    loopPackageRatesAndEqualsCheapest();
    if ( sortByInput === 'price') {
      reorderReplies();
      mergeReplies();
    }
    loopAndBuildHelperOutput();

    return chunk;

  };
};
<commit_msg>WEB-8064: make bandTypes dynamic<commit_after>'use strict';

var _ = require('lodash');
var sortBy = require('../lib/sortBy');

module.exports = function(dust) {

  /*
  * @description A filter that nests package rates under theatre sections - Needs to be moved
  * @param {object} packageRates Package Rates
  * @param {object} venueProducts venue products
  * @param {object} ticketRates ticket rates
  * @param {object} roomRates room rates
  * @example {@_filterSeats packageRates=packageRatesReply.packageRates venueProducts=venueProductsReply.venueProducts roomRates=packageRatesReply.linked.roomRates ticketRates=packageRatesReply.linked.ticketRates} {/_filterSeats} output loop of sections with seats inside sorted by price
  */

  dust.helpers._filterSeats = function(chunk, context, bodies, params) {
    params = params || {};

    var packageRates = params.packageRates;
    var venueProduct = _.head(params.venueProducts);
    var ticketRates = params.ticketRates;
    var roomRates = params.roomRates;
    var bandColours = params.bandColours;
    var sortByInput = params.sortBy;

    var cheapestRoom = {
      id: 0,
      price: -1
    };

    var reply = {};

    function stubReply() {
      // Stub reply for rates filling in
      // Make new node for every section a theatre has.
      _.forEach(venueProduct.sections, function(section, i) {
        var item = {
          name: section,
          rates: []
        };
        reply[i] = item;
      });
    }

    stubReply();

    // Sort packageRates by Price Ascending.
    var sortedPackageRates = sortBy(packageRates, 'grossPrice');

    function findCheapestRoom() {
      // Find cheapest room rate ID
      _.forEach(roomRates, function(roomRate) {
        if (roomRate.grossPrice < cheapestRoom.price || cheapestRoom.price === -1) {
          cheapestRoom = {
            id: roomRate.id,
            price: roomRate.grossPrice
          };
        }
      });
    }

    // use Transformer show config to add Gold, Silver or Bronze to packageRate
    function assignColoursToBands(packageRate) {
      // assign gold, silver, bronze to packages depending on their current priceBand
      _.forEach(bandColours, function(sectionValue, sectionKey) {
        // check for match between transformer and existing data
        // check for section that might not have full title ( e.g. Grand Circle in Grand Circle (Left) )
        if (ticketRates[packageRate.ids].section === sectionKey || ticketRates[packageRate.ids].section.indexOf( sectionKey ) > -1 ) {
          // match transformer config to existing seat section
          _.forEach(sectionValue, function(bandValue, bandKey) {
            if (ticketRates[packageRate.ids].priceBand === bandKey) {
              // match transformer config to existing priceBand
              ticketRates[packageRate.ids].colour = bandValue;
              packageRate.colour = bandValue;
            }
          });
        }
      });
    }

    function loopReplySections(packageRate) {
      // For every section in the stubbed out reply
      // check if the current package rate is in the current section
      _.forEach(reply, function(item, i) {
        // If so then we want to add this packageRate under the theatre section.
        if (ticketRates[packageRate.links.ticketRates.ids].section === item.name) {
          // WEB-8081
          if (bandColours !== null) {
            assignColoursToBands(packageRate.links.ticketRates);
          }
          reply[i].rates.push(packageRate);
        }
      });
    }

    function loopPackageRatesAndEqualsCheapest() {
      // Loop through the sorted PackageRates
      _.forEach(sortedPackageRates, function(packageRate) {
        // If it has the cheapest room rate keep it, otherwise do nothing.
        if (_.head(packageRate.links.roomRates.ids) === cheapestRoom.id) {
          loopReplySections(packageRate);
        }
      });
    }

    function reorderRates(rates, replyIndex) {
      var bandTypes = {};
      _.forEach(bandColours, function (sectionObj, sectionKey) {
        _.forEach(sectionObj, function (quality, bandLetter) {
          bandTypes[quality] = [];
        });
      });

      var newRates = [];

      // 1 - Push each rate to its own colour array (bandTypes)
      _.forEach(rates, function(rate) {
        var quality = rate.links.ticketRates.colour;
        if (!quality) {
          return;
        }
        bandTypes[quality].push(rate);
      });
      // 2 - Order each bandTypes array by price and from each ordered array
      // pick the cheapest rate and push it into the newRates array
      _.forEach(bandTypes, function(bandType, key) {
        bandType = _.orderBy(bandType, function(rate) {
          return rate.grossPrice;
        }, ['asc']);

        if (bandType.length) {
          bandTypes[key] = bandType;
          newRates.push(bandType[0]);
        }
      });
      // 3 - Order the cheapest rates selected in the step before by price
      newRates = _.orderBy(newRates, function(newRate) {
        return newRate.grossPrice;
      }, ['asc']);

      reply[replyIndex].rates = newRates;
    }
    // For each reply we want to order the inner 'rates' array by price
    // and then we want to reorder the replies themselves always by price
    // taking in consideration just the cheapest rate of each
    function reorderReplies() {
      _.forEach(reply, function(section, i) {
        reorderRates(section.rates, i);
      });
    }

    function getMergedName(currSectionName, nextSectionName) {
      var _name1 = currSectionName
        .toUpperCase()
        .replace(' ', '')
        .replace('(LEFT)', '')
        .replace('(RIGHT)', '');

      var _name2 = nextSectionName
        .toUpperCase()
        .replace(' ', '')
        .replace('(LEFT)', '')
        .replace('(RIGHT)', '');

      if (_name1 !== _name2) {
        return false;
      }

      return currSectionName
        .replace('(Left)', '')
        .replace('(Right)', '')
        .trim();
    }

    function getMergedSection(currSection, nextSection) {
      var mergedReply = currSection;
      var newName = getMergedName(currSection.name, nextSection.name);
      if (!newName) {
        return false;
      }

      var mixedRates = currSection.rates.concat(nextSection.rates);
      var newRatesObj = {};

      for (var i = 0; i < mixedRates.length; i++) {
        var currRate = mixedRates[i];
        var currColour = currRate.links.ticketRates.colour;

        var condition = !newRatesObj[currColour]
            || (currRate.grossPrice < newRatesObj[currColour].grossPrice)
            || ((currRate.grossPrice === newRatesObj[currColour].grossPrice) && Math.random() >= 0.5);

        if (condition) {
          newRatesObj[currColour] = currRate;
        }
      }

      mergedReply.name = newName;
      mergedReply.rates = _.values(newRatesObj);

      return mergedReply;
    }

    function mergeReplies() {
      var newReply = {};
      var loopMax = _.size(reply);

      for (var i = 0, newReplyIndex = 0; i < loopMax; i++, newReplyIndex++) {
        if (!reply[i + 1]) {
          newReply[newReplyIndex] = reply[i];
          break;
        }

        var mergedSection = getMergedSection(reply[i], reply[i + 1]);

        if (mergedSection) {
          newReply[newReplyIndex] = mergedSection;
          i++;
        } else {
          newReply[newReplyIndex] = reply[i];
        }
      }

      reply = newReply;
    }

    function loopAndBuildHelperOutput() {
      // Loop through finalised reply and add each theatre section to context for looping in template
      _.forEach(reply, function(item) {
        chunk = chunk.render(bodies.block, context.push(item));
      });
    }

    findCheapestRoom();
    loopPackageRatesAndEqualsCheapest();
    if ( sortByInput === 'price') {
      reorderReplies();
      mergeReplies();
    }
    loopAndBuildHelperOutput();

    return chunk;

  };
};
<|endoftext|>