<commit_before>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var exports = module.exports;
var exec = require('cordova/exec');
var storage = require('org.chromium.storage.Storage');
var helpers = require('org.chromium.common.helpers');
var platform = require('cordova/platform');
var channel = require('cordova/channel');
var Event = require('org.chromium.common.events');
var useNativeAlarms = platform.id == 'android';
var alarms = Object.create(null);
var alarmsToFireOnStartUp = [];

function makeAlarm(name, scheduledTime, periodInMinutes, timeoutId) {
    var alarm = { };
    alarm.name = name;
    alarm.scheduledTime = scheduledTime;
    alarm.periodInMinutes = periodInMinutes;
    alarm.timeoutId = timeoutId;
    return alarm;
}

function triggerAlarm(name) {
    if (!(name in alarms)) {
        return;
    }
    alarm = alarms[name];
    if (alarm.periodInMinutes) {
        alarm.scheduledTime += alarm.periodInMinutes*60000;
        if (!useNativeAlarms) {
            alarm.timeoutId = setTimeout(function() { triggerAlarm(name) }, alarm.scheduledTime - Date.now());
        }
    } else {
        delete alarms[name];
    }
    storage.internal.set({'alarms':alarms});
    exports.onAlarm.fire(alarm);
}

exports.create = function(name, alarmInfo) {
    if (typeof alarmInfo == 'undefined') {
        alarmInfo = name;
        name = '';
    }

    if ('delayInMinutes' in alarmInfo) {
        if(alarmInfo.delayInMinutes < .0042) { // about 0.25 seconds
            console.error('Error during alarms.create: delayInMinutes too small, using .0042');
            alarmInfo.delayInMinutes = .0042;
        }
    }
    if ('periodInMinutes' in alarmInfo) {
        if(alarmInfo.periodInMinutes < .0042) { // about 0.25 seconds
            console.error('Error during alarms.create: periodInMinutes too small, using .0042');
            alarmInfo.periodInMinutes = .0042;
        }
    }

    var when;
    if ('when' in alarmInfo) {
        when = alarmInfo.when;
        if ('delayInMinutes' in alarmInfo) {
            throw new Error('Error during alarms.create: Cannot set both when and delayInMinutes.');
        }
    } else if ('delayInMinutes' in alarmInfo) {
        when = Date.now() + alarmInfo.delayInMinutes*60000;
    } else if ('periodInMinutes' in alarmInfo) {
        when = Date.now() + alarmInfo.periodInMinutes*60000;
    } else {
        console.error('Error during alarms.create: Must set at least one of when, delayInMinutes, or periodInMinutes');
        return;
    }

    if (useNativeAlarms) {
        alarms[name] = makeAlarm(name, when, alarmInfo.periodInMinutes);
        exec(undefined, undefined, 'ChromeAlarms', 'create', [name, when, alarmInfo.periodInMinutes]);
    } else {
        if (name in alarms) {
            clearTimeout(alarms[name].timeoutId);
        }
        var timeoutId = setTimeout(function() { triggerAlarm(name) }, when - Date.now());
        alarms[name] = makeAlarm(name, when, alarmInfo.periodInMinutes, timeoutId);
    }
    storage.internal.set({'alarms':alarms});
}

exports.get = function(name, callback) {
    if (typeof callback == 'undefined') {
        callback = name;
        name = '';
    }
    if (!(name in alarms)) {
        console.error('Error during alarms.get: No alarm named \'' + name + '\' exists.');
        callback();
        return;
    }
    setTimeout(function() {
        callback(alarms[name])
    }, 0);
}

exports.getAll = function(callback) {
    var ret = [];
    for (var name in alarms) {
        ret.push(alarms[name]);
    }
    setTimeout(function() {
       callback(ret);
    }, 0);
}

exports.clear = function clear(name) {
    if (typeof name == 'undefined') {
        name = '';
    }
    if (!(name in alarms)) {
        console.error('Error during alarms.clear: No alarm named \'' + name + '\' exists.');
        return;
    }

    if (useNativeAlarms) {
        exec(undefined, undefined, 'ChromeAlarms', 'clear', [[name]]);
    } else {
        clearTimeout(alarms[name].timeoutId);
    }
    delete alarms[name];
    storage.internal.set({'alarms':alarms});
}

exports.clearAll = function() {
    var names = Object.keys(alarms);
    if (useNativeAlarms) {
        alarms = Object.create(null);
        exec(undefined, undefined, 'ChromeAlarms', 'clear', [names]);
    } else {
        names.forEach(function(name) {
            exports.clear(name);
        });
    }
    storage.internal.set({'alarms':alarms});
}

exports.onAlarm = new Event('onAlarm');

function reregisterAlarms() {
    // Iterate over Object.keys(alarms) rather than using a normal
    // for (var name in alarms) loop because alarms can be deleted as we are
    // iterating in the case where a non-repeating alarm gets fired.
    Object.keys(alarms).forEach(function(name) {
        var scheduledTime = alarms[name].scheduledTime;
        if (Date.now() > scheduledTime) {
            // The behavior of desktop chrome if we missed firing an alarm at the scheduled time is to fire
            // the alarm initially and if periodInMinutes is set, then schedule so that the firing points
            // are aligned with what they originally were when the alarm was previously created. We try to
            // emulate this behavior here.
            alarmsToFireOnStartUp.push(name);
            if (!(name in alarms)) {
                return;
            }
            clearTimeout(alarms[name].timeoutId);
            var periodInMillis = alarms[name].periodInMinutes*60000;
            scheduledTime += Math.ceil((Date.now() - scheduledTime)/periodInMillis)*periodInMillis;
        }
        var alarmCreateInfo = {'when':scheduledTime};
        if ('periodInMinutes' in alarms[name]) {
            alarmCreateInfo.periodInMinutes = alarms[name].periodInMinutes;
        }
        delete alarms[name];
        exports.create(name, alarmCreateInfo);
    });
}

function firePendingAlarms() {
    var alarmId;
    while (alarmId = alarmsToFireOnStartUp.shift()) {
        triggerAlarm(alarmId);
    }
    alarmsToFireOnStartUp = null;
}

function onMessageFromNative(msg) {
    if (msg.charAt(0) === 'f') {
        var alarmId = msg.slice(1);
        if (alarmsToFireOnStartUp) {
            alarmsToFireOnStartUp.push(alarmId);
        } else {
            triggerAlarm(alarmId);
        }
    } else {
        throw new Error('alarms unknown message: ' + msg);
    }
}

channel.createSticky('onChromeAlarmsReady');
channel.waitForInitialization('onChromeAlarmsReady');
channel.onCordovaReady.subscribe(function() {
    storage.internal.get('alarms', function(values) {
        alarms = values.alarms || {};
        alarms.__proto__ = null;

        if (useNativeAlarms) {
            exec(onMessageFromNative, undefined, 'ChromeAlarms', 'messageChannel', []);
        } else {
            reregisterAlarms();
        }

        helpers.runAtStartUp(function() {
            if (alarmsToFireOnStartUp.length) {
                helpers.queueLifeCycleEvent(firePendingAlarms);
            } else {
                alarmsToFireOnStartUp = null;
            }
        });
        channel.initializationComplete('onChromeAlarmsReady');
    });
});
<commit_msg>Add callback parameters to `chrome.alarms.clear` and `clearAll` (fixes #489)<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var exports = module.exports;
var exec = require('cordova/exec');
var storage = require('org.chromium.storage.Storage');
var helpers = require('org.chromium.common.helpers');
var platform = require('cordova/platform');
var channel = require('cordova/channel');
var Event = require('org.chromium.common.events');
var useNativeAlarms = platform.id == 'android';
var alarms = Object.create(null);
var alarmsToFireOnStartUp = [];

function makeAlarm(name, scheduledTime, periodInMinutes, timeoutId) {
    var alarm = { };
    alarm.name = name;
    alarm.scheduledTime = scheduledTime;
    alarm.periodInMinutes = periodInMinutes;
    alarm.timeoutId = timeoutId;
    return alarm;
}

function triggerAlarm(name) {
    if (!(name in alarms)) {
        return;
    }
    alarm = alarms[name];
    if (alarm.periodInMinutes) {
        alarm.scheduledTime += alarm.periodInMinutes*60000;
        if (!useNativeAlarms) {
            alarm.timeoutId = setTimeout(function() { triggerAlarm(name) }, alarm.scheduledTime - Date.now());
        }
    } else {
        delete alarms[name];
    }
    saveAlarms(function() {
        exports.onAlarm.fire(alarm);
    });
}

function saveAlarms(callback) {
    storage.internal.set({'alarms':alarms}, callback && function() {
        callback();
    });
}

exports.create = function(name, alarmInfo) {
    if (typeof alarmInfo == 'undefined') {
        alarmInfo = name;
        name = '';
    }

    if ('delayInMinutes' in alarmInfo) {
        if(alarmInfo.delayInMinutes < .0042) { // about 0.25 seconds
            console.error('Error during alarms.create: delayInMinutes too small, using .0042');
            alarmInfo.delayInMinutes = .0042;
        }
    }
    if ('periodInMinutes' in alarmInfo) {
        if(alarmInfo.periodInMinutes < .0042) { // about 0.25 seconds
            console.error('Error during alarms.create: periodInMinutes too small, using .0042');
            alarmInfo.periodInMinutes = .0042;
        }
    }

    var when;
    if ('when' in alarmInfo) {
        when = alarmInfo.when;
        if ('delayInMinutes' in alarmInfo) {
            throw new Error('Error during alarms.create: Cannot set both when and delayInMinutes.');
        }
    } else if ('delayInMinutes' in alarmInfo) {
        when = Date.now() + alarmInfo.delayInMinutes*60000;
    } else if ('periodInMinutes' in alarmInfo) {
        when = Date.now() + alarmInfo.periodInMinutes*60000;
    } else {
        console.error('Error during alarms.create: Must set at least one of when, delayInMinutes, or periodInMinutes');
        return;
    }

    if (useNativeAlarms) {
        alarms[name] = makeAlarm(name, when, alarmInfo.periodInMinutes);
        exec(undefined, undefined, 'ChromeAlarms', 'create', [name, when, alarmInfo.periodInMinutes]);
    } else {
        if (name in alarms) {
            clearTimeout(alarms[name].timeoutId);
        }
        var timeoutId = setTimeout(function() { triggerAlarm(name) }, when - Date.now());
        alarms[name] = makeAlarm(name, when, alarmInfo.periodInMinutes, timeoutId);
    }
    saveAlarms();
}

exports.get = function(name, callback) {
    if (typeof callback == 'undefined') {
        callback = name;
        name = '';
    }
    if (!(name in alarms)) {
        console.error('Error during alarms.get: No alarm named \'' + name + '\' exists.');
        callback();
        return;
    }
    setTimeout(function() {
        callback(alarms[name])
    }, 0);
}

exports.getAll = function(callback) {
    var ret = [];
    for (var name in alarms) {
        ret.push(alarms[name]);
    }
    setTimeout(function() {
       callback(ret);
    }, 0);
}

exports.clear = function clear(name, callback) {
    if (typeof name == 'undefined') {
        name = '';
    }
    if (!(name in alarms)) {
        console.error('Error during alarms.clear: No alarm named \'' + name + '\' exists.');
        return;
    }

    delete alarms[name];
    if (useNativeAlarms) {
        exec(function() {
            saveAlarms(callback)
        }, undefined, 'ChromeAlarms', 'clear', [[name]]);
    } else {
        clearTimeout(alarms[name].timeoutId);
        saveAlarms(callback);
    }
};

exports.clearAll = function(callback) {
    var names = Object.keys(alarms);
    if (useNativeAlarms) {
        alarms = Object.create(null);
        exec(function() {
            saveAlarms(callback);
        }, undefined, 'ChromeAlarms', 'clear', [names]);
    } else {
        names.forEach(function(name) {
            exports.clear(name);
        });
        saveAlarms(callback);
    }
};

exports.onAlarm = new Event('onAlarm');

function reregisterAlarms() {
    // Iterate over Object.keys(alarms) rather than using a normal
    // for (var name in alarms) loop because alarms can be deleted as we are
    // iterating in the case where a non-repeating alarm gets fired.
    Object.keys(alarms).forEach(function(name) {
        var scheduledTime = alarms[name].scheduledTime;
        if (Date.now() > scheduledTime) {
            // The behavior of desktop chrome if we missed firing an alarm at the scheduled time is to fire
            // the alarm initially and if periodInMinutes is set, then schedule so that the firing points
            // are aligned with what they originally were when the alarm was previously created. We try to
            // emulate this behavior here.
            alarmsToFireOnStartUp.push(name);
            if (!(name in alarms)) {
                return;
            }
            clearTimeout(alarms[name].timeoutId);
            var periodInMillis = alarms[name].periodInMinutes*60000;
            scheduledTime += Math.ceil((Date.now() - scheduledTime)/periodInMillis)*periodInMillis;
        }
        var alarmCreateInfo = {'when':scheduledTime};
        if ('periodInMinutes' in alarms[name]) {
            alarmCreateInfo.periodInMinutes = alarms[name].periodInMinutes;
        }
        delete alarms[name];
        exports.create(name, alarmCreateInfo);
    });
}

function firePendingAlarms() {
    var alarmId;
    while (alarmId = alarmsToFireOnStartUp.shift()) {
        triggerAlarm(alarmId);
    }
    alarmsToFireOnStartUp = null;
}

function onMessageFromNative(msg) {
    if (msg.charAt(0) === 'f') {
        var alarmId = msg.slice(1);
        if (alarmsToFireOnStartUp) {
            alarmsToFireOnStartUp.push(alarmId);
        } else {
            triggerAlarm(alarmId);
        }
    } else {
        throw new Error('alarms unknown message: ' + msg);
    }
}

channel.createSticky('onChromeAlarmsReady');
channel.waitForInitialization('onChromeAlarmsReady');
channel.onCordovaReady.subscribe(function() {
    storage.internal.get('alarms', function(values) {
        alarms = values.alarms || {};
        alarms.__proto__ = null;

        if (useNativeAlarms) {
            exec(onMessageFromNative, undefined, 'ChromeAlarms', 'messageChannel', []);
        } else {
            reregisterAlarms();
        }

        helpers.runAtStartUp(function() {
            if (alarmsToFireOnStartUp.length) {
                helpers.queueLifeCycleEvent(firePendingAlarms);
            } else {
                alarmsToFireOnStartUp = null;
            }
        });
        channel.initializationComplete('onChromeAlarmsReady');
    });
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import { FormattedMessage } from 'react-intl';
import styled from 'styled-components';
import CreateRelatedMedia from './CreateRelatedMedia';
import MediaRoute from '../../relay/MediaRoute';
import mediaFragment from '../../relay/mediaFragment';
import MediaDetail from './MediaDetail';
import MediasLoading from './MediasLoading';
import CheckContext from '../../CheckContext';
import { getFilters } from '../../helpers';
import {
  FlexRow,
  black54,
  black87,
  body1,
  subheading2,
  units,
} from '../../styles/js/shared';

const StyledHeaderRow = styled.div`
  justify-content: space-between;
  display: flex;
  color: ${black54};
  font: ${body1};
  height: ${units(6)};

  h2 {
    color: ${black87};
    flex: 1;
    font: ${subheading2};
    margin: 0;
  }
`;

const previousFilters = {};

class MediaRelatedComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  componentDidMount() {
    this.subscribe();
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  getContext() {
    return new CheckContext(this).getContextStore();
  }

  subscribe() {
    const { pusher } = this.getContext();
    if (pusher) {
      pusher.subscribe(this.props.media.pusher_channel).bind('relationship_change', (data) => {
        const relationship = JSON.parse(data.message);
        if (
          (this.getContext().clientSessionId !== data.actor_session_id) &&
          (relationship.source_id === this.props.media.dbid)
        ) {
          this.props.relay.forceFetch();
        }
      });
    }
  }

  unsubscribe() {
    const { pusher } = this.getContext();
    if (pusher) {
      pusher.unsubscribe(this.props.media.pusher_channel);
    }
  }

  render() {
    const filters = getFilters();
    const { dbid } = this.props.media;

    let medias = [];
    const { relationships } = this.props.media;
    const { targets_count, sources_count } = relationships;
    const targets = relationships.targets.edges;
    const sources = relationships.sources.edges;
    let filtered_count = 0;
    const total = targets_count + sources_count;

    if (filters !== previousFilters[dbid]) {
      previousFilters[dbid] = filters;
      this.props.relay.setVariables({ filters });
      this.props.relay.forceFetch();
    } else if (targets.length > 0) {
      medias = targets[0].node.targets.edges;
      filtered_count = total - medias.length;
    } else if (sources.length > 0) {
      medias.push({ node: sources[0].node.source });
      sources[0].node.siblings.edges.forEach((sibling) => {
        if (sibling.node.dbid !== this.props.media.dbid) {
          medias.push(sibling);
        }
      });
    }

    return (

        { this.props.showHeader ?
           : null }

        { (this.props.showNumbers && medias.length > 0) ?
           : null }

        <FlexRow>

            {medias.map((item) => {
              if (item.node.archived) {
                return null;
              }
              return (

                  {<MediaDetail media={item.node} condensed parentComponent={this} hideRelated />}
                  {
}

              );
            })}

        </FlexRow>

    );
  }
}

MediaRelatedComponent.contextTypes = {
  store: PropTypes.object,
};

const MediaRelatedContainer = Relay.createContainer(MediaRelatedComponent, {
  initialVariables: {
    contextId: null,
    filters: getFilters(),
  },
  fragments: {
    media: () => Relay.QL`
      fragment on ProjectMedia {
        id
        dbid
        archived
        permissions
        pusher_channel
        media {
          quote
        }
        project {
          dbid
          search_id
          permissions
          team {
            search_id
            verification_statuses
            translation_statuses
          }
        }
        relationships {
          target_id
          source_id
          targets_count
          sources_count
          targets(first: 10000, filters: $filters) {
            edges {
              node {
                id
                type
                targets(first: 10000) {
                  edges {
                    node {
                      ${mediaFragment}
                    }
                  }
                }
              }
            }
          }
          sources(first: 10000) {
            edges {
              node {
                id
                type
                siblings(first: 10000) {
                  edges {
                    node {
                      ${mediaFragment}
                    }
                  }
                }
                source {
                  ${mediaFragment}
                }
              }
            }
          }
        }
      }
    `,
  },
});

const MediaRelated = (props) => {
  const ids = `${props.media.dbid},${props.media.project_id}`;
  const route = new MediaRoute({ ids });

  return (
    <Relay.RootContainer
      Component={MediaRelatedContainer}
      renderFetched={data => <MediaRelatedContainer {...props} {...data} />}
      route={route}
      renderLoading={() => <MediasLoading count={1} />}
    />
  );
};

export default MediaRelated;
<commit_msg>#7256 Fix add related alignment<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import { FormattedMessage } from 'react-intl';
import styled from 'styled-components';
import CreateRelatedMedia from './CreateRelatedMedia';
import MediaRoute from '../../relay/MediaRoute';
import mediaFragment from '../../relay/mediaFragment';
import MediaDetail from './MediaDetail';
import MediasLoading from './MediasLoading';
import CheckContext from '../../CheckContext';
import { getFilters } from '../../helpers';
import {
  FlexRow,
  black54,
  black87,
  body1,
  subheading2,
  units,
} from '../../styles/js/shared';

const StyledHeaderRow = styled.div`
  justify-content: space-between;
  display: flex;
  color: ${black54};
  font: ${body1};
  height: ${units(6)};

  h2 {
    color: ${black87};
    flex: 1;
    font: ${subheading2};
    margin: 0;
  }
`;

const previousFilters = {};

class MediaRelatedComponent extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  componentDidMount() {
    this.subscribe();
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  getContext() {
    return new CheckContext(this).getContextStore();
  }

  subscribe() {
    const { pusher } = this.getContext();
    if (pusher) {
      pusher.subscribe(this.props.media.pusher_channel).bind('relationship_change', (data) => {
        const relationship = JSON.parse(data.message);
        if (
          (this.getContext().clientSessionId !== data.actor_session_id) &&
          (relationship.source_id === this.props.media.dbid)
        ) {
          this.props.relay.forceFetch();
        }
      });
    }
  }

  unsubscribe() {
    const { pusher } = this.getContext();
    if (pusher) {
      pusher.unsubscribe(this.props.media.pusher_channel);
    }
  }

  render() {
    const filters = getFilters();
    const { dbid } = this.props.media;

    let medias = [];
    const { relationships } = this.props.media;
    const { targets_count, sources_count } = relationships;
    const targets = relationships.targets.edges;
    const sources = relationships.sources.edges;
    let filtered_count = 0;
    const total = targets_count + sources_count;

    if (filters !== previousFilters[dbid]) {
      previousFilters[dbid] = filters;
      this.props.relay.setVariables({ filters });
      this.props.relay.forceFetch();
    } else if (targets.length > 0) {
      medias = targets[0].node.targets.edges;
      filtered_count = total - medias.length;
    } else if (sources.length > 0) {
      medias.push({ node: sources[0].node.source });
      sources[0].node.siblings.edges.forEach((sibling) => {
        if (sibling.node.dbid !== this.props.media.dbid) {
          medias.push(sibling);
        }
      });
    }

    return (

        { this.props.showHeader ?
           : null }

        { (this.props.showNumbers && medias.length > 0) ?
           : null }

        <FlexRow>

            {medias.map((item) => {
              if (item.node.archived) {
                return null;
              }
              return (

                  {<MediaDetail media={item.node} condensed parentComponent={this} hideRelated />}
                  {
}

              );
            })}

        </FlexRow>

    );
  }
}

MediaRelatedComponent.contextTypes = {
  store: PropTypes.object,
};

const MediaRelatedContainer = Relay.createContainer(MediaRelatedComponent, {
  initialVariables: {
    contextId: null,
    filters: getFilters(),
  },
  fragments: {
    media: () => Relay.QL`
      fragment on ProjectMedia {
        id
        dbid
        archived
        permissions
        pusher_channel
        media {
          quote
        }
        project {
          dbid
          search_id
          permissions
          team {
            search_id
            verification_statuses
            translation_statuses
          }
        }
        relationships {
          target_id
          source_id
          targets_count
          sources_count
          targets(first: 10000, filters: $filters) {
            edges {
              node {
                id
                type
                targets(first: 10000) {
                  edges {
                    node {
                      ${mediaFragment}
                    }
                  }
                }
              }
            }
          }
          sources(first: 10000) {
            edges {
              node {
                id
                type
                siblings(first: 10000) {
                  edges {
                    node {
                      ${mediaFragment}
                    }
                  }
                }
                source {
                  ${mediaFragment}
                }
              }
            }
          }
        }
      }
    `,
  },
});

const MediaRelated = (props) => {
  const ids = `${props.media.dbid},${props.media.project_id}`;
  const route = new MediaRoute({ ids });

  return (
    <Relay.RootContainer
      Component={MediaRelatedContainer}
      renderFetched={data => <MediaRelatedContainer {...props} {...data} />}
      route={route}
      renderLoading={() => <MediasLoading count={1} />}
    />
  );
};

export default MediaRelated;
<|endoftext|>


<commit_before>// Factory to add, remove, edit and delete maintenence requests:
( function(){
  'use strict';

  angular.module( 'dolmen.services', [ 'firebase' ] )
  .service( 'Maintenance', [
    '$rootScope',
    'FBDolmen',
    '$firebaseArray',
    '$firebaseObject',
    function( $rootScope, FBDolmen, $firebaseArray, $firebaseObject ) {
    var maintObj = { };
    var ref = new Firebase( FBDolmen + '/requests' );
    maintObj.getRequests = function( oid ){
      ref.orderByChild( 'oid' ).equalTo( oid ).on( 'value', function( res ){
        var reqList = res.val();
        maintObj.requests = reqList;
        $rootScope.$broadcast( 'requestsRetrieved' );
      });
    };
    maintObj.addRequest = function( request ){
      // from firebase docs:
      var list = $firebaseArray(ref);
      list.$add( request ).then(function(ref) {
        var id = ref.key();
        list.$indexFor(id); // returns location in the array
      });
    };
    maintObj.updateRequest = function( request ){
      var reqRef = new Firebase( FBDolmen + '/requests/' + request.dbId );
      var req = $firebaseObject( reqRef );
      req.address = request.address;
      req.status = request.status;
      req.description = request.description;
      req.tenant = req.tenant;
      req.phone = req.phone;
      return req.$save();
    }
    return maintObj;
  }]);

  })();
<commit_msg>added missing semi-colon<commit_after>// Factory to add, remove, edit and delete maintenence requests:
( function(){
  'use strict';

  angular.module( 'dolmen.services', [ 'firebase' ] )
  .service( 'Maintenance', [
    '$rootScope',
    'FBDolmen',
    '$firebaseArray',
    '$firebaseObject',
    function( $rootScope, FBDolmen, $firebaseArray, $firebaseObject ) {
    var maintObj = { };
    var ref = new Firebase( FBDolmen + '/requests' );
    maintObj.getRequests = function( oid ){
      ref.orderByChild( 'oid' ).equalTo( oid ).on( 'value', function( res ){
        var reqList = res.val();
        maintObj.requests = reqList;
        $rootScope.$broadcast( 'requestsRetrieved' );
      });
    };
    maintObj.addRequest = function( request ){
      // from firebase docs:
      var list = $firebaseArray(ref);
      list.$add( request ).then(function(ref) {
        var id = ref.key();
        list.$indexFor(id); // returns location in the array
      });
    };
    maintObj.updateRequest = function( request ){
      var reqRef = new Firebase( FBDolmen + '/requests/' + request.dbId );
      var req = $firebaseObject( reqRef );
      req.address = request.address;
      req.status = request.status;
      req.description = request.description;
      req.tenant = req.tenant;
      req.phone = req.phone;
      return req.$save();
    };
    return maintObj;
  }]);

  })();
<|endoftext|>


<commit_before>var config = new Config();
var uninstallList = [];

window.addEventListener("load", function(ev) {
  config.load();
  loadControls();

  if (!config.scripts.length == 0) {
    populateChooser();
    chooseScript(0);
  }
}, false);

function handleOkButton() {
  for (var i = 0, script = null; (script = uninstallList[i]); i++) {
    var idx = config.find(script.namespace, script.name);
    config.scripts.splice(idx, 1);
  }
  config.save();

  var chkUninstallPrefs = document.getElementById('chkUninstallPrefs');
  for (var i = 0, script = null; (script = uninstallList[i]); i++) {
    file = getScriptBasedir(script);
    file.normalize();
    if(file.path != getScriptDir().path){
      if (file.exists()) {
        file.remove(true); // file==base directory recursive delete
      }
    } else {
      file = getScriptFile(script);
      if (file.exists()) {
        file.remove(false);
      }
    }
    if (chkUninstallPrefs.checked) {
       // Remove saved preferences
       var scriptPrefRoot = ["scriptvals.",
                  script.namespace,
                  "/",
                  script.name,
                  "."].join("");
       GM_prefRoot.remove(scriptPrefRoot);
    }
  }
  return true;
};

var listbox, header, description, chkEnabled, btnEdit, btnUninstall;
var selectedScript;
var pagesControl;

function loadControls() {
  listbox = document.getElementById("lstScripts");
  header = document.getElementById("ctlHeader");
  description = document.getElementById("ctlDescription");
  btnEdit = document.getElementById("btnEdit");
  btnUninstall = document.getElementById("btnUninstall");
  pagesControl = new PagesControl(document.getElementById("pages-control"));
  chkEnabled = document.getElementById("chkEnabled");

  listbox.addEventListener("select", function() { updateDetails(); }, false);
  btnEdit.addEventListener("command", function() { handleEditButton(); }, false);
  btnUninstall.addEventListener("command", function() { handleUninstallButton(); }, false);
  chkEnabled.addEventListener("command", function() {
     if (selectedScript) {
       selectedScript.enabled = chkEnabled.checked;
       if (selectedScript.enabled) {
         listbox.selectedItem.style.color = '';
       } else {
         listbox.selectedItem.style.color = 'gray';
       }
     }
  }, false);
};

function updateDetails() {
  if (listbox.selectedCount == 0) {
    selectedScript = null;
    header.textContent = " ";
    description.textContent = " ";
    chkEnabled.checked = true;
    pagesControl.clear();
    document.documentElement.getButton("accept").disabled = false;
  } else {
    selectedScript = listbox.getSelectedItem(0).script;

    // make sure one word isn't too long to fit ... a too-long word
    // will bump the interface out wider than the window
    var wordLen = 50;
    var desc = selectedScript.description.split(/\s+/);
    for (var i = 0; i < desc.length; i++) {
      if (desc[i].length > wordLen) {
        for (var j = desc[i].length; j > 0; j -= wordLen) {
          desc[i] = desc[i].substr(0,j) + '\u200B' + desc[i].substr(j);
        }
      }
    }
    desc = desc.join(' ');

    header.textContent = selectedScript.name;
    description.textContent = desc;
    chkEnabled.checked = selectedScript.enabled;
    pagesControl.populate(selectedScript);
  }
};

function handleEditButton() {
  openInEditor(getScriptFile(selectedScript),
  document.getElementById("gm-manage-bundle").getString("editor.prompt"));
};

function handleUninstallButton() {
  var index=listbox.selectedIndex;

  // mark script to be uninstalled on "OK"
  uninstallList.push(selectedScript);

  // remove it from the display
  listbox.removeChild(listbox.childNodes[index]);

  if (listbox.childNodes.length > 0) {
    chooseScript(Math.max(Math.min(listbox.selectedIndex, listbox.childNodes.length - 1), 0));
  }
};

function populateChooser() {
  for (var i = 0, script = null; (script = config.scripts[i]); i++) {
    var listitem = document.createElement("listitem");

    listitem.setAttribute("label", script.name);
    listitem.setAttribute("crop", "end");
    listitem.script = script;
    listitem.index = i;

    if (!script.enabled) {
      listitem.style.color = 'gray';
    }

  listbox.appendChild(listitem);
  }
};

function chooseScript(index) {
  listbox.selectedIndex = index;
  listbox.focus();
};

function toggleScript(index, enableFlag) {
  var listitem = listbox.childNodes[index];
  if (enableFlag) {
    listitem.script.enabled = true;
    listitem.style.color = '';
  } else {
    listitem.script.enabled = false;
    listitem.style.color = 'gray';
  }
};

function reorderScript(from, to) {
  // make sure to and from are in range
  if (from < 0 || to < 0 ||
    from > config.scripts.length || to > config.scripts.length
  ) {
    return false;
  }

  // REORDER CONFIG:
  // save item-to-move
  var tmp = config.scripts[from];
  // remove it
  config.scripts.splice(from, 1);
  // put it back in the new spot
  config.scripts.splice(to, 0, tmp);

  // REORDER DISPLAY:
  var tmp = listbox.childNodes[from];
  listbox.removeChild(tmp);
  listbox.insertBefore(tmp, listbox.childNodes[to]);
  // fix the listbox indexes
  for (var i=0, node=null; node=listbox.childNodes[i]; i++) {
    node.index=i;
  }

  // then re-select the dropped script
  listbox.selectedIndex = to;

  return true;
};

// allow reordering scripts with keyboard (alt- up and down)
function listboxKeypress(event) {
  if (0 == listbox.selectedCount) return;
  if (!event.altKey) return;

  var index = listbox.selectedIndex;

  if (KeyEvent.DOM_VK_UP == event.keyCode) {
    if (0 == index) return;

    !reorderScript(index, index - 1);
    listbox.selectedIndex = index - 1;
  } else if (KeyEvent.DOM_VK_DOWN == event.keyCode) {
    if (index == config.scripts.length - 1) return;

    !reorderScript(index, index + 1);
    listbox.selectedIndex = index + 1;
  }
};

// allow reordering scripts with drag-and-drop
var dndObserver = {
  lastFeedbackIndex: null,

  getSupportedFlavours: function () {
    var flavours = new FlavourSet();
    flavours.appendFlavour("text/unicode");
    return flavours;
  },

  onDragStart: function (event, transferData, action) {
    if ('listitem' != event.target.tagName ) return false;

    transferData.data = new TransferData();
    transferData.data.addDataForFlavour("text/unicode", event.target.index);

    return true;
  },

  onDragOver: function (event, flavour, session) {
    if (listbox.selectedIndex == event.target.index) {
      this.clearFeedback();
      return false;
    }

    return this.setFeedback(event);
  },

  onDrop: function (event, dropdata, session) {
    // clean up the feedback
    this.lastFeedbackIndex = null;
    this.clearFeedback();

    // figure out how to move
    var newIndex = this.findNewIndex(event);
    if (null === newIndex) return;
    var index = parseInt(dropdata.data);
    if (newIndex > index) newIndex--;

    // do the move
    reorderScript(index, newIndex);
  },

  //////////////////////////////////////////////////////////////////////////////

  setFeedback: function(event) {
    var newIndex = this.findNewIndex(event);

    // don't do anything if we haven't changed
    if (newIndex === this.lastFeedbackIndex) return false; // NOTE: possible incongruent logic
    this.lastFeedbackIndex = newIndex;

    // clear any previous feedback
    this.clearFeedback();

    // and set the current feedback
    if (null === newIndex) {
      return false;
    } else if (listbox.selectedIndex == newIndex) {
      return false;
    } else {
      if (0 == newIndex) {
        listbox.firstChild.setAttribute('dragover', 'top');
      } else if (newIndex >= listbox.childNodes.length) {
        listbox.lastChild.setAttribute('dragover', 'bottom');
      } else {
        listbox.childNodes[newIndex - 1].setAttribute('dragover', 'bottom');
      }
    }

    return true;
  },

  clearFeedback: function() {
    var box = document.getElementById('lstScripts');
    for (var i = 0, el; el = box.childNodes[i]; i++) {
      el.removeAttribute('dragover');
    }
  },

  findNewIndex: function(event) {
    var target = event.target;

    // not in the list box? forget it!
    if (listbox != target && listbox != target.parentNode) return null;

    var targetBox = target.boxObject
      .QueryInterface(Components.interfaces.nsIBoxObject);

    if (listbox == target) {
      // here, we are hovering over the listbox, not a particular listitem
      // check if we are very near the top (y + 4), return zero, else return end
      if (event.clientY < targetBox.y + 4) {
        return 0;
      } else {
        return listbox.childNodes.length;
      }
    } else {
      var targetMid = targetBox.y + (targetBox.height / 2);

      if (event.clientY >= targetMid) {
        return target.index + 1;
      } else {
        return target.index;
      }
    }

    // should never get here, but in case
    return null;
  }
};<commit_msg>Merging changesets 564 and 565 to trunk. These fix ticket 63, which was that the entire gm_scripts folder could get deleted on osx in some situations.<commit_after>var config = new Config();
var uninstallList = [];

window.addEventListener("load", function(ev) {
  config.load();
  loadControls();

  if (!config.scripts.length == 0) {
    populateChooser();
    chooseScript(0);
  }
}, false);

function handleOkButton() {
  for (var i = 0, script = null; (script = uninstallList[i]); i++) {
    var idx = config.find(script.namespace, script.name);
    config.scripts.splice(idx, 1);
  }
  config.save();

  var chkUninstallPrefs = document.getElementById('chkUninstallPrefs');
  for (var i = 0, script = null; (script = uninstallList[i]); i++) {
    file = getScriptBasedir(script);
    if (!file.equals(getScriptDir())) {
      if (file.exists()) {
        file.remove(true); // file==base directory recursive delete
      }
    } else {
      file = getScriptFile(script);
      if (file.exists()) {
        file.remove(false);
      }
    }
    if (chkUninstallPrefs.checked) {
       // Remove saved preferences
       var scriptPrefRoot = ["scriptvals.",
                  script.namespace,
                  "/",
                  script.name,
                  "."].join("");
       GM_prefRoot.remove(scriptPrefRoot);
    }
  }
  return true;
};

var listbox, header, description, chkEnabled, btnEdit, btnUninstall;
var selectedScript;
var pagesControl;

function loadControls() {
  listbox = document.getElementById("lstScripts");
  header = document.getElementById("ctlHeader");
  description = document.getElementById("ctlDescription");
  btnEdit = document.getElementById("btnEdit");
  btnUninstall = document.getElementById("btnUninstall");
  pagesControl = new PagesControl(document.getElementById("pages-control"));
  chkEnabled = document.getElementById("chkEnabled");

  listbox.addEventListener("select", function() { updateDetails(); }, false);
  btnEdit.addEventListener("command", function() { handleEditButton(); }, false);
  btnUninstall.addEventListener("command", function() { handleUninstallButton(); }, false);
  chkEnabled.addEventListener("command", function() {
     if (selectedScript) {
       selectedScript.enabled = chkEnabled.checked;
       if (selectedScript.enabled) {
         listbox.selectedItem.style.color = '';
       } else {
         listbox.selectedItem.style.color = 'gray';
       }
     }
  }, false);
};

function updateDetails() {
  if (listbox.selectedCount == 0) {
    selectedScript = null;
    header.textContent = " ";
    description.textContent = " ";
    chkEnabled.checked = true;
    pagesControl.clear();
    document.documentElement.getButton("accept").disabled = false;
  } else {
    selectedScript = listbox.getSelectedItem(0).script;

    // make sure one word isn't too long to fit ... a too-long word
    // will bump the interface out wider than the window
    var wordLen = 50;
    var desc = selectedScript.description.split(/\s+/);
    for (var i = 0; i < desc.length; i++) {
      if (desc[i].length > wordLen) {
        for (var j = desc[i].length; j > 0; j -= wordLen) {
          desc[i] = desc[i].substr(0,j) + '\u200B' + desc[i].substr(j);
        }
      }
    }
    desc = desc.join(' ');

    header.textContent = selectedScript.name;
    description.textContent = desc;
    chkEnabled.checked = selectedScript.enabled;
    pagesControl.populate(selectedScript);
  }
};

function handleEditButton() {
  openInEditor(getScriptFile(selectedScript),
  document.getElementById("gm-manage-bundle").getString("editor.prompt"));
};

function handleUninstallButton() {
  var index=listbox.selectedIndex;

  // mark script to be uninstalled on "OK"
  uninstallList.push(selectedScript);

  // remove it from the display
  listbox.removeChild(listbox.childNodes[index]);

  if (listbox.childNodes.length > 0) {
    chooseScript(Math.max(Math.min(listbox.selectedIndex, listbox.childNodes.length - 1), 0));
  }
};

function populateChooser() {
  for (var i = 0, script = null; (script = config.scripts[i]); i++) {
    var listitem = document.createElement("listitem");

    listitem.setAttribute("label", script.name);
    listitem.setAttribute("crop", "end");
    listitem.script = script;
    listitem.index = i;

    if (!script.enabled) {
      listitem.style.color = 'gray';
    }

  listbox.appendChild(listitem);
  }
};

function chooseScript(index) {
  listbox.selectedIndex = index;
  listbox.focus();
};

function toggleScript(index, enableFlag) {
  var listitem = listbox.childNodes[index];
  if (enableFlag) {
    listitem.script.enabled = true;
    listitem.style.color = '';
  } else {
    listitem.script.enabled = false;
    listitem.style.color = 'gray';
  }
};

function reorderScript(from, to) {
  // make sure to and from are in range
  if (from < 0 || to < 0 ||
    from > config.scripts.length || to > config.scripts.length
  ) {
    return false;
  }

  // REORDER CONFIG:
  // save item-to-move
  var tmp = config.scripts[from];
  // remove it
  config.scripts.splice(from, 1);
  // put it back in the new spot
  config.scripts.splice(to, 0, tmp);

  // REORDER DISPLAY:
  var tmp = listbox.childNodes[from];
  listbox.removeChild(tmp);
  listbox.insertBefore(tmp, listbox.childNodes[to]);
  // fix the listbox indexes
  for (var i=0, node=null; node=listbox.childNodes[i]; i++) {
    node.index=i;
  }

  // then re-select the dropped script
  listbox.selectedIndex = to;

  return true;
};

// allow reordering scripts with keyboard (alt- up and down)
function listboxKeypress(event) {
  if (0 == listbox.selectedCount) return;
  if (!event.altKey) return;

  var index = listbox.selectedIndex;

  if (KeyEvent.DOM_VK_UP == event.keyCode) {
    if (0 == index) return;

    !reorderScript(index, index - 1);
    listbox.selectedIndex = index - 1;
  } else if (KeyEvent.DOM_VK_DOWN == event.keyCode) {
    if (index == config.scripts.length - 1) return;

    !reorderScript(index, index + 1);
    listbox.selectedIndex = index + 1;
  }
};

// allow reordering scripts with drag-and-drop
var dndObserver = {
  lastFeedbackIndex: null,

  getSupportedFlavours: function () {
    var flavours = new FlavourSet();
    flavours.appendFlavour("text/unicode");
    return flavours;
  },

  onDragStart: function (event, transferData, action) {
    if ('listitem' != event.target.tagName ) return false;

    transferData.data = new TransferData();
    transferData.data.addDataForFlavour("text/unicode", event.target.index);

    return true;
  },

  onDragOver: function (event, flavour, session) {
    if (listbox.selectedIndex == event.target.index) {
      this.clearFeedback();
      return false;
    }

    return this.setFeedback(event);
  },

  onDrop: function (event, dropdata, session) {
    // clean up the feedback
    this.lastFeedbackIndex = null;
    this.clearFeedback();

    // figure out how to move
    var newIndex = this.findNewIndex(event);
    if (null === newIndex) return;
    var index = parseInt(dropdata.data);
    if (newIndex > index) newIndex--;

    // do the move
    reorderScript(index, newIndex);
  },

  //////////////////////////////////////////////////////////////////////////////

  setFeedback: function(event) {
    var newIndex = this.findNewIndex(event);

    // don't do anything if we haven't changed
    if (newIndex === this.lastFeedbackIndex) return false; // NOTE: possible incongruent logic
    this.lastFeedbackIndex = newIndex;

    // clear any previous feedback
    this.clearFeedback();

    // and set the current feedback
    if (null === newIndex) {
      return false;
    } else if (listbox.selectedIndex == newIndex) {
      return false;
    } else {
      if (0 == newIndex) {
        listbox.firstChild.setAttribute('dragover', 'top');
      } else if (newIndex >= listbox.childNodes.length) {
        listbox.lastChild.setAttribute('dragover', 'bottom');
      } else {
        listbox.childNodes[newIndex - 1].setAttribute('dragover', 'bottom');
      }
    }

    return true;
  },

  clearFeedback: function() {
    var box = document.getElementById('lstScripts');
    for (var i = 0, el; el = box.childNodes[i]; i++) {
      el.removeAttribute('dragover');
    }
  },

  findNewIndex: function(event) {
    var target = event.target;

    // not in the list box? forget it!
    if (listbox != target && listbox != target.parentNode) return null;

    var targetBox = target.boxObject
      .QueryInterface(Components.interfaces.nsIBoxObject);

    if (listbox == target) {
      // here, we are hovering over the listbox, not a particular listitem
      // check if we are very near the top (y + 4), return zero, else return end
      if (event.clientY < targetBox.y + 4) {
        return 0;
      } else {
        return listbox.childNodes.length;
      }
    } else {
      var targetMid = targetBox.y + (targetBox.height / 2);

      if (event.clientY >= targetMid) {
        return target.index + 1;
      } else {
        return target.index;
      }
    }

    // should never get here, but in case
    return null;
  }
};<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from './template';

import loadAll from 'ember-osf/utils/load-relationship';
import outsideClick from 'ember-osf/utils/outside-click';

/**
 * File browser widget
 *
 * Sample usage:
 * ```handlebars
 * {{file-browser}}
 * ```
 * @class file-browser
 */
export default Ember.Component.extend({
    // TODO: Improve documentation in the future
    layout,
    //Can be overwritten to have a trimmed down display, these are all the options available to be displayed
    display: Ember.A(['header', 'share-link-column', 'size-column', 'version-column', 'downloads-column', 'modified-column', 'delete-button', 'rename-button', 'download-button', 'view-button', 'info-button', 'upload-button']),
    store: Ember.inject.service(),
    toast: Ember.inject.service(),
    classNames: ['file-browser'],
    dropzoneOptions: {
        createImageThumbnails: false,
        method: 'PUT',
        withCredentials: true,
        preventMultipleFiles: true,
        acceptDirectories: false
    },
    init() {
        this._super(...arguments);
        this.set('_items', Ember.A());
        outsideClick(function() {
            this.send('dismissOtherPops');
        }.bind(this));
        Ember.$(window).resize(function() {
            this.send('dismissOtherPops');
        }.bind(this));
    },
    currentUser: Ember.inject.service(),
    edit: Ember.computed('user', function() {
        return this.get('user.id') === this.get('currentUser.currentUserId');
    }),
    _loadFiles(user) {
        //pagination? when?
        loadAll(user, 'quickfiles', this.get('_items')).then(() => {
            this.set('loaded', true);
            this.set('_items', this.get('_items').sortBy('itemName'));
        });
    },
    _loadUser:  Ember.on('init', Ember.observer('user', function() {
        let user = this.get('user');
        if (!user || this.get('loaded')) {
            return;
        }
        //items need to be reloaded when attrs are received
        //TODO: think about replacing _items with user.items, provided it's loaded properly
        let _load = user_ => {
            Ember.run(() => {
                this.set('_items', Ember.A());
                Ember.run.next(() => {
                    this._loadFiles(user_);
                });
            });
        };
        if (user.then) {
            user.then(user_ => {
                _load(user_);
            })
        } else {
            _load(user);
        }
    })),
    uploadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.upload.href');
    }),
    downloadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.download.href');
    }),
    loaded: false,
    filtering: false,
    renaming: false,
    sortingBy: 'itemName',
    sortingOrder: 'asc',
    uploading: Ember.A(),
    filter: null,
    modalOpen: false,
    itemsLoaded: true,
    selectedItems: Ember.computed.filterBy('items', 'isSelected', true),
    loadedChanged: Ember.observer('itemsLoaded', function() {
        let containerWidth = this.$().width();
        this.set('itemWidth', containerWidth);
    }),
    flash(item, message, type, time) {
        item.set('flash', {
            message,
            type: type || 'success'
        });
        Ember.run.later(() => item.set('flash', null), time || 2000);
    },
    items: Ember.computed('_items', 'textValue', 'filtering', 'sortingBy', 'sortingOrder', function() {
        //look at ways to use the api to filter
        let items = this.get('textValue') && this.get('filtering') ? this.get('_items').filter(i => i.get('name').toLowerCase().indexOf(this.get('textValue').toLowerCase()) !== -1) : this.get('_items');
        let sorted = items.sortBy(this.get('sortingBy'));
        return this.get('sortingOrder') === 'des' ? sorted.reverse() : sorted;
    }),
    textFieldOpen: Ember.computed('filtering', 'renaming', function() {
        return this.get('filtering') ? 'filtering' : (this.get('renaming') ? 'renaming' : false);
    }),
    nameColumnWidth: Ember.computed('display', function() {
        let display = this.get('display');
        let width = 6 + !display.includes('share-link-column') + !display.includes('size-column') + !display.includes('version-column') + !display.includes('downloads-column') + 2 * !display.includes('modified-column');
        if (!display.includes('header')) { //Allows scrollable elements to use extra space occupied by header
            let height = Ember.$('.file-browser-list').height();
            Ember.$('.file-browser-list').height(height + 50);
        }
        return width;
    }),
    browserState: Ember.computed('loaded', '_items', function() {
        return this.get('loaded') ? (this.get('_items').length ? (this.get('items').length ? 'show' : 'filtered') : 'empty') : 'loading';
    }),
    actions: {
        //dropzone listeners
        addedFile(_, __, file) {
            this.get('uploading').pushObject(file);
        },
        uploadProgress(_, __, file, progress) {
            Ember.$('#uploading-' + file.size).css('width', progress + '%');
        },
        dragStart() {
            this.set('dropping', true);
        },
        dragEnd() {
            this.set('dropping', false);
        },
        error(_, __, file, response) {
            this.get('uploading').removeObject(file);
            this.get('toast').error(response);
        },
        success(_, __, file, response) {
            this.get('uploading').removeObject(file);
            let data = response.data.attributes;
            //OPTIONS (some not researched)
            //Ember store method for passing updated attributes (either with a query for the object, or iterating to find matching)
            //Manually updating the object based on new attrs
            //Making an additional request anytime success is done, finding the file detail page based on path
            let path = data.path; //THERE SHOULD BE A BETTER WAY OF DOING THIS
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (path === file.get('path')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                conflictingItem.setProperties({
                    size: data.size,
                    currentVersion: data.extra.version,
                    dateModified: data.modified_utc
                })
                return;
            }
            response.data.type = 'file'; //
            response.data.attributes.currentVersion = '1';
            // Strip prefix.
            response.data.id = response.data.id.replace(/^[^\/]*\//, '');
            let item = this.get('store').push(response);
            item.set('links', response.data.links); //Push doesnt pass it links
            this.get('_items').unshiftObject(item);
            this.notifyPropertyChange('_items');
            Ember.run.next(() => {
                this.flash(item, 'This file has been added.');
                this.get('toast').success('A file has been added');
            });
        },
        buildUrl(files) {
            let name = files[0].name;
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (name === file.get('itemName')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                return conflictingItem.get('links.upload') + '?kind=file';
            }
            return this.get('uploadUrl') + '?' + Ember.$.param({
                name: files[0].name
            });
        },
        selectItem(item) {
            this.set('renaming', false);
            if (this.get('selectedItems.length') > 1) {
                for (var item_ of this.get('selectedItems')) {
                    item_.set('isSelected', item_ === item);
                }
            } else if (this.get('selectedItems.length') ===  1) {
                if (item.get('isSelected')) {
                    item.set('isSelected', false);
                    return;
                }
                let otherItem = this.get('selectedItems.firstObject');
                otherItem.set('isSelected', false);
            }
            item.set('isSelected', true);
            this.set('shiftAnchor', item);
        },
        selectMultiple(item, toggle) {
            this.set('renaming', false);
            if (toggle) {
                item.toggleProperty('isSelected');
            } else {
                let items = this.get('items');
                let anchor = this.get('shiftAnchor');
                if (anchor) {
                    let max = Math.max(items.indexOf(anchor), items.indexOf(item));
                    let min = Math.min(items.indexOf(anchor), items.indexOf(item));
                    for (var item_ of this.get('items')) {
                        item_.set('isSelected', item_ === item || item_ === anchor || (items.indexOf(item_) > min && items.indexOf(item_) < max));
                    }
                }
                item.set('isSelected', true);
            }
            Ember.run.next(this, function(){
                if (this.get('selectedItems.length') === 1) {
                    this.set('shiftAnchor', item)
                }
            });
        },
        viewItem() {
            let item = this.get('selectedItems.firstObject');
            this.sendAction('openFile', item);
        },
        openItem(item, qparams) {
            this.sendAction('openFile', item, qparams);
        },
        downloadItem() {
            let downloadLink = this.get('selectedItems.firstObject.links.download');
            window.location = downloadLink;
        },
        _deleteItem(item) {
            item.destroyRecord().then(() => {
                this.flash(item, 'This file has been deleted.', 'danger');
                Ember.run.later(() => {
                    this.get('_items').removeObject(item);
                    this.notifyPropertyChange('_items');
                }, 1800);
            }).catch(() => this.flash(item, 'Delete failed.', 'danger'));
        },
        deleteItem(){
            this.send('_deleteItem', this.get('selectedItems.firstObject'));
            this.set('modalOpen', false);
        },
        deleteItems() {
            for (let item_ of this.get('selectedItems')) {
                this.send('_deleteItem', item_);
            }
            this.set('modalOpen', false);
        },
        _rename(conflict) {
            let item = this.get('selectedItems.firstObject');
            this.set('modalOpen', false);
            item.rename(this.get('textValue'), conflict).then(() => {
                this.flash(item, 'Successfully renamed');
                if (conflict === 'replace') {
                    const replacedItem  = this.get('_conflictingItem');
                    if (!replacedItem) {
                        return;
                    }
                    this.flash(replacedItem, 'This file has been replaced', 'danger');
                    setTimeout(() => {
                        this.get('_items').removeObject(replacedItem);
                        this.notifyPropertyChange('_items');
                    }, 1800);
                    // Later to avoid flash() trying to set on a destroyed item.
                    Ember.run.later(() => replacedItem.unloadRecord(), 2200);
                }
            }).catch(() => this.flash(item, 'Failed to rename item', 'danger'));
            this.set('textValue', null);
            this.toggleProperty('renaming');
        },
        rename() {
            let rename = this.get('textValue');
            let conflict = false;
            for (let item of this.get('_items')) {
                if (item.get('itemName') === rename) {
                    if (item === this.get('selectedItems.firstObject')) {
                        this.set('textValue', null);
                        this.toggleProperty('renaming');
                        return;
                    }
                    conflict = true;
                    this.set('_conflictingItem', item);
                    break;
                }
            }
            if (conflict) {
                this.set('modalOpen', 'renameConflict');
            } else {
                this.send('_rename');
            }
        },
        cancelRename() {
            this.set('textValue', null);
            this.toggleProperty('renaming')
            this.set('modalOpen', false);
        },
        sort(by, order) {
            this.$('.sorting').removeClass('active');
            this.$(`.${by}${order}`).addClass('active');
            this.setProperties({
                sortingBy: by,
                sortingOrder: order
            })
        },
        toggleText(which) {
            this.set('textValue', which === 'renaming' ? this.get('selectedItems.firstObject.itemName') : null);
            this.toggleProperty(which);
        },
        openModal(modalType) {
            this.set('modalOpen', modalType);
        },
        closeModal() {
            this.set('modalOpen', false);
        },
        textValueKeypress() {
            if (this.get('renaming')) {
                this.send('rename');
            }
        },
        dismissOtherPops(item) {
            for (var item_ of this.get('items')) {
                if (!item || item_.get('path') !== item.get('path')) {
                    item_.set('visiblePopup', false);
                }
            }
        }
    }
});
<commit_msg>Add properties to file-browser to control select behavior<commit_after>import Ember from 'ember';
import layout from './template';

import loadAll from 'ember-osf/utils/load-relationship';
import outsideClick from 'ember-osf/utils/outside-click';

/**
 * File browser widget
 *
 * Sample usage:
 * ```handlebars
 * {{file-browser}}
 * ```
 * @class file-browser
 */
export default Ember.Component.extend({
    // TODO: Improve documentation in the future
    layout,
    //Can be overwritten to have a trimmed down display, these are all the options available to be displayed
    display: Ember.A(['header', 'share-link-column', 'size-column', 'version-column', 'downloads-column', 'modified-column', 'delete-button', 'rename-button', 'download-button', 'view-button', 'info-button', 'upload-button']),
    store: Ember.inject.service(),
    toast: Ember.inject.service(),
    classNames: ['file-browser'],
    dropzoneOptions: {
        createImageThumbnails: false,
        method: 'PUT',
        withCredentials: true,
        preventMultipleFiles: true,
        acceptDirectories: false
    },
    multiple: true,
    unselect: true,
    openOnSelect: false,
    init() {
        this._super(...arguments);
        this.set('_items', Ember.A());
        outsideClick(function() {
            this.send('dismissOtherPops');
        }.bind(this));
        Ember.$(window).resize(function() {
            this.send('dismissOtherPops');
        }.bind(this));
    },
    currentUser: Ember.inject.service(),
    edit: Ember.computed('user', function() {
        return this.get('user.id') === this.get('currentUser.currentUserId');
    }),
    _loadFiles(user) {
        //pagination? when?
        loadAll(user, 'quickfiles', this.get('_items')).then(() => {
            this.set('loaded', true);
            this.set('_items', this.get('_items').sortBy('itemName'));
        });
    },
    _loadUser:  Ember.on('init', Ember.observer('user', function() {
        let user = this.get('user');
        if (!user || this.get('loaded')) {
            return;
        }
        //items need to be reloaded when attrs are received
        //TODO: think about replacing _items with user.items, provided it's loaded properly
        let _load = user_ => {
            Ember.run(() => {
                this.set('_items', Ember.A());
                Ember.run.next(() => {
                    this._loadFiles(user_);
                });
            });
        };
        if (user.then) {
            user.then(user_ => {
                _load(user_);
            })
        } else {
            _load(user);
        }
    })),
    uploadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.upload.href');
    }),
    downloadUrl: Ember.computed('user', function() {
        return this.get('user.links.relationships.quickfiles.links.download.href');
    }),
    loaded: false,
    filtering: false,
    renaming: false,
    sortingBy: 'itemName',
    sortingOrder: 'asc',
    uploading: Ember.A(),
    filter: null,
    modalOpen: false,
    itemsLoaded: true,
    selectedItems: Ember.computed.filterBy('items', 'isSelected', true),
    loadedChanged: Ember.observer('itemsLoaded', function() {
        let containerWidth = this.$().width();
        this.set('itemWidth', containerWidth);
    }),
    flash(item, message, type, time) {
        item.set('flash', {
            message,
            type: type || 'success'
        });
        Ember.run.later(() => item.set('flash', null), time || 2000);
    },
    items: Ember.computed('_items', 'textValue', 'filtering', 'sortingBy', 'sortingOrder', function() {
        //look at ways to use the api to filter
        let items = this.get('textValue') && this.get('filtering') ? this.get('_items').filter(i => i.get('name').toLowerCase().indexOf(this.get('textValue').toLowerCase()) !== -1) : this.get('_items');
        let sorted = items.sortBy(this.get('sortingBy'));
        return this.get('sortingOrder') === 'des' ? sorted.reverse() : sorted;
    }),
    textFieldOpen: Ember.computed('filtering', 'renaming', function() {
        return this.get('filtering') ? 'filtering' : (this.get('renaming') ? 'renaming' : false);
    }),
    nameColumnWidth: Ember.computed('display', function() {
        let display = this.get('display');
        let width = 6 + !display.includes('share-link-column') + !display.includes('size-column') + !display.includes('version-column') + !display.includes('downloads-column') + 2 * !display.includes('modified-column');
        if (!display.includes('header')) { //Allows scrollable elements to use extra space occupied by header
            let height = Ember.$('.file-browser-list').height();
            Ember.$('.file-browser-list').height(height + 50);
        }
        return width;
    }),
    browserState: Ember.computed('loaded', '_items', function() {
        return this.get('loaded') ? (this.get('_items').length ? (this.get('items').length ? 'show' : 'filtered') : 'empty') : 'loading';
    }),
    actions: {
        //dropzone listeners
        addedFile(_, __, file) {
            this.get('uploading').pushObject(file);
        },
        uploadProgress(_, __, file, progress) {
            Ember.$('#uploading-' + file.size).css('width', progress + '%');
        },
        dragStart() {
            this.set('dropping', true);
        },
        dragEnd() {
            this.set('dropping', false);
        },
        error(_, __, file, response) {
            this.get('uploading').removeObject(file);
            this.get('toast').error(response);
        },
        success(_, __, file, response) {
            this.get('uploading').removeObject(file);
            let data = response.data.attributes;
            //OPTIONS (some not researched)
            //Ember store method for passing updated attributes (either with a query for the object, or iterating to find matching)
            //Manually updating the object based on new attrs
            //Making an additional request anytime success is done, finding the file detail page based on path
            let path = data.path; //THERE SHOULD BE A BETTER WAY OF DOING THIS
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (path === file.get('path')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                conflictingItem.setProperties({
                    size: data.size,
                    currentVersion: data.extra.version,
                    dateModified: data.modified_utc
                })
                return;
            }
            response.data.type = 'file'; //
            response.data.attributes.currentVersion = '1';
            // Strip prefix.
            response.data.id = response.data.id.replace(/^[^\/]*\//, '');
            let item = this.get('store').push(response);
            item.set('links', response.data.links); //Push doesnt pass it links
            this.get('_items').unshiftObject(item);
            this.notifyPropertyChange('_items');
            Ember.run.next(() => {
                this.flash(item, 'This file has been added.');
                this.get('toast').success('A file has been added');
            });
        },
        buildUrl(files) {
            let name = files[0].name;
            let conflictingItem = false;
            for (let file of this.get('_items')) {
                if (name === file.get('itemName')) {
                    conflictingItem = file;
                    break;
                }
            }
            if (conflictingItem) {
                return conflictingItem.get('links.upload') + '?kind=file';
            }
            return this.get('uploadUrl') + '?' + Ember.$.param({
                name: files[0].name
            });
        },
        selectItem(item) {
            if (this.get('openOnSelect')) {
                this.sendAction('openFile', item);
            }
            this.set('renaming', false);
            if (this.get('selectedItems.length') > 1) {
                for (var item_ of this.get('selectedItems')) {
                    item_.set('isSelected', item_ === item);
                }
            } else if (this.get('selectedItems.length') ===  1) {
                if (item.get('isSelected') && this.get('unselect')) {
                    item.set('isSelected', false);
                    return;
                }
                let otherItem = this.get('selectedItems.firstObject');
                otherItem.set('isSelected', false);
            }
            item.set('isSelected', true);
            this.set('shiftAnchor', item);
        },
        selectMultiple(item, toggle) {
            if (!this.get('multiple')) {
                return;
            }
            this.set('renaming', false);
            if (toggle) {
                item.toggleProperty('isSelected');
            } else {
                let items = this.get('items');
                let anchor = this.get('shiftAnchor');
                if (anchor) {
                    let max = Math.max(items.indexOf(anchor), items.indexOf(item));
                    let min = Math.min(items.indexOf(anchor), items.indexOf(item));
                    for (var item_ of this.get('items')) {
                        item_.set('isSelected', item_ === item || item_ === anchor || (items.indexOf(item_) > min && items.indexOf(item_) < max));
                    }
                }
                item.set('isSelected', true);
            }
            Ember.run.next(this, function(){
                if (this.get('selectedItems.length') === 1) {
                    this.set('shiftAnchor', item)
                }
            });
        },
        viewItem() {
            let item = this.get('selectedItems.firstObject');
            this.sendAction('openFile', item);
        },
        openItem(item, qparams) {
            this.sendAction('openFile', item, qparams);
        },
        downloadItem() {
            let downloadLink = this.get('selectedItems.firstObject.links.download');
            window.location = downloadLink;
        },
        _deleteItem(item) {
            item.destroyRecord().then(() => {
                this.flash(item, 'This file has been deleted.', 'danger');
                Ember.run.later(() => {
                    this.get('_items').removeObject(item);
                    this.notifyPropertyChange('_items');
                }, 1800);
            }).catch(() => this.flash(item, 'Delete failed.', 'danger'));
        },
        deleteItem(){
            this.send('_deleteItem', this.get('selectedItems.firstObject'));
            this.set('modalOpen', false);
        },
        deleteItems() {
            for (let item_ of this.get('selectedItems')) {
                this.send('_deleteItem', item_);
            }
            this.set('modalOpen', false);
        },
        _rename(conflict) {
            let item = this.get('selectedItems.firstObject');
            this.set('modalOpen', false);
            item.rename(this.get('textValue'), conflict).then(() => {
                this.flash(item, 'Successfully renamed');
                if (conflict === 'replace') {
                    const replacedItem  = this.get('_conflictingItem');
                    if (!replacedItem) {
                        return;
                    }
                    this.flash(replacedItem, 'This file has been replaced', 'danger');
                    setTimeout(() => {
                        this.get('_items').removeObject(replacedItem);
                        this.notifyPropertyChange('_items');
                    }, 1800);
                    // Later to avoid flash() trying to set on a destroyed item.
                    Ember.run.later(() => replacedItem.unloadRecord(), 2200);
                }
            }).catch(() => this.flash(item, 'Failed to rename item', 'danger'));
            this.set('textValue', null);
            this.toggleProperty('renaming');
        },
        rename() {
            let rename = this.get('textValue');
            let conflict = false;
            for (let item of this.get('_items')) {
                if (item.get('itemName') === rename) {
                    if (item === this.get('selectedItems.firstObject')) {
                        this.set('textValue', null);
                        this.toggleProperty('renaming');
                        return;
                    }
                    conflict = true;
                    this.set('_conflictingItem', item);
                    break;
                }
            }
            if (conflict) {
                this.set('modalOpen', 'renameConflict');
            } else {
                this.send('_rename');
            }
        },
        cancelRename() {
            this.set('textValue', null);
            this.toggleProperty('renaming')
            this.set('modalOpen', false);
        },
        sort(by, order) {
            this.$('.sorting').removeClass('active');
            this.$(`.${by}${order}`).addClass('active');
            this.setProperties({
                sortingBy: by,
                sortingOrder: order
            })
        },
        toggleText(which) {
            this.set('textValue', which === 'renaming' ? this.get('selectedItems.firstObject.itemName') : null);
            this.toggleProperty(which);
        },
        openModal(modalType) {
            this.set('modalOpen', modalType);
        },
        closeModal() {
            this.set('modalOpen', false);
        },
        textValueKeypress() {
            if (this.get('renaming')) {
                this.send('rename');
            }
        },
        dismissOtherPops(item) {
            for (var item_ of this.get('items')) {
                if (!item || item_.get('path') !== item.get('path')) {
                    item_.set('visiblePopup', false);
                }
            }
        }
    }
});
<|endoftext|>


<commit_before>import Rx from 'rx';
import _ from 'lodash';
import { Trade, TradeNotification } from '../../services/model';
import { logger } from '../';

const _log:logger.Logger = logger.create('OpenFin');

const REQUEST_LIMIT_CHECK_TOPIC = 'request-limit-check';

export default class OpenFin {

  tradeClickedSubject:Rx.Subject<string>;
  limitCheckSubscriber:string;
  limitCheckId:number;

  constructor() {
    this.tradeClickedSubject = new Rx.Subject();
    this.limitCheckId = 1;
    this._initializeLimitChecker();
  }

  get isRunningInOpenFin() {
    return typeof fin !== 'undefined';
  }

  close(window = this._currentWindow){
    window.close(true, () => _log.info('Window closed with success.'), err => _log.error('Failed to close window.', err));
  }

  minimize(window = this._currentWindow){
   window.minimize(() => _log.info('Window minimized with success.'), err => _log.error('Failed to minimize window.', err));
  }

  maximize(window = this._currentWindow){
    window.getState(state => {
        switch (state){
          case 'maximized':
          case 'restored':
          case 'minimized':
            window.restore(() => window.bringToFront());
            break;
          default:
            window.maximize(() => _log.info('Window maximized with success.'), err => _log.error('Failed to maximize window.', err));
        }
      });
  }

  checkLimit(executablePrice, notional:number, tradedCurrencyPair:string):Rx.Observable<boolean> {
    let _this = this;
    return Rx.Observable.create(observer => {
        let disposables = new Rx.CompositeDisposable();
        if (_this.limitCheckSubscriber == null) {
          _log.debug('client side limit check not up, will delegate to to server');
          observer.onNext(true);
          observer.onCompleted();
        } else {
          _log.debug(`checking if limit is ok with ${_this.limitCheckSubscriber}`);
          let topic = 'limit-check-response' + (_this.limitCheckId++);
          let limitCheckResponse:(msg:any) => void = (msg) => {
            _log.debug(`${_this.limitCheckSubscriber} limit check response was ${msg}`);
            observer.onNext(msg.result);
            observer.onCompleted();
          };

          fin.desktop.InterApplicationBus.subscribe(_this.limitCheckSubscriber, topic, limitCheckResponse);

          fin.desktop.InterApplicationBus.send(_this.limitCheckSubscriber, REQUEST_LIMIT_CHECK_TOPIC, {
            id: _this.limitCheckId,
            responseTopic: topic,
            tradedCurrencyPair: tradedCurrencyPair,
            notional: notional,
            rate: executablePrice
          });

          disposables.add(Rx.Disposable.create(() => {
            fin.desktop.InterApplicationBus.unsubscribe(_this.limitCheckSubscriber, topic, limitCheckResponse);
          }));
        }
        return disposables;
      });
  }

  get _currentWindow() {
    return fin.desktop.Window.getCurrent();
  }

  displayCurrencyChart(symbol){
    let chartIqAppId = 'ChartIQ';
    fin.desktop.System.getAllApplications((apps) => {
      let chartIqApp = _.find(apps, ((app) => {
        return app.isRunning && app.uuid === chartIqAppId;
      }));
      if(chartIqApp) {
        this._refreshCurrencyChart(symbol);
      } else {
        this._launchCurrencyChart(symbol);
      }
    });
  }

  /**
   * Initialize limit checker
   * @private
   */
  _initializeLimitChecker() {
    fin.desktop.main(() => {
      fin.desktop.InterApplicationBus.addSubscribeListener(function(uuid, topic) {
        if (topic === REQUEST_LIMIT_CHECK_TOPIC) {
          _log.info(`${uuid} has subscribed as a limit checker`);
          // There will only be one. If there are more, last subscriber will be used
          this.limitCheckSubscriber = uuid;
        }
      }.bind(this));
      fin.desktop.InterApplicationBus.addUnsubscribeListener(function(uuid, topic) {
        if (topic === REQUEST_LIMIT_CHECK_TOPIC) {
          _log.info(`${uuid} has unsubscribed as a limit checker`);
          this.limitCheckSubscriber = null;
        }
      }.bind(this));
    });
  }

  _refreshCurrencyChart(symbol){
    let interval = 5;
    let chartIqAppId = 'ChartIQ';
    fin.desktop.InterApplicationBus.publish('chartiq:main:change_symbol', { symbol: symbol, interval: interval });
  }

  _launchCurrencyChart(symbol){
    let interval = 5;
    let chartIqAppId = 'ChartIQ';
    let url = `http://openfin.chartiq.com/0.5/chartiq-shim.html?symbol=${symbol}&period=${interval}`;
    let name = `chartiq_${(new Date()).getTime()}`;
    const applicationIcon = 'http://openfin.chartiq.com/0.5/img/openfin-logo.png';
    let app = new fin.desktop.Application({
      uuid: chartIqAppId,
      url: url,
      name: name,
      applicationIcon: applicationIcon,
      mainWindowOptions:{
        autoShow: false
      }
    }, function(){
      app.run();
    });
  }

  openTradeNotification(trade:Trade): void {
    if (!this.isRunningInOpenFin) return;

    let tradeNotification = new TradeNotification(trade);

    let notification = new fin.desktop.Notification({
      url: '/notification.html',
      message: tradeNotification,
      onMessage: () => {
        this.maximise();
      }
    });
  }
}
<commit_msg>minor tweaks<commit_after>import Rx from 'rx';
import _ from 'lodash';
import { Trade, TradeNotification } from '../../services/model';
import { logger } from '../';

const _log:logger.Logger = logger.create('OpenFin');

const REQUEST_LIMIT_CHECK_TOPIC = 'request-limit-check';

export default class OpenFin {

  tradeClickedSubject:Rx.Subject<string>;
  limitCheckSubscriber:string;
  limitCheckId:number;

  constructor() {
    this.tradeClickedSubject = new Rx.Subject();
    this.limitCheckId = 1;
    this._initializeLimitChecker();
  }

  get isRunningInOpenFin() {
    return typeof fin !== 'undefined';
  }

  close(window = this._currentWindow){
    window.close(true, () => _log.info('Window closed with success.'), err => _log.error('Failed to close window.', err));
  }

  minimize(window = this._currentWindow){
    window.minimize(() => _log.info('Window minimized with success.'), err => _log.error('Failed to minimize window.', err));
  }

  maximize(window = this._currentWindow){
    window.getState(state => {
        switch (state){
          case 'maximized':
          case 'restored':
          case 'minimized':
            window.restore(() => window.bringToFront());
            break;
          default:
            window.maximize(() => _log.info('Window maximized with success.'), err => _log.error('Failed to maximize window.', err));
        }
      });
  }

  checkLimit(executablePrice, notional:number, tradedCurrencyPair:string):Rx.Observable<boolean> {
    let _this = this;
    return Rx.Observable.create(observer => {
        let disposables = new Rx.CompositeDisposable();
        if (_this.limitCheckSubscriber === null) {
          _log.debug('client side limit check not up, will delegate to to server');
          observer.onNext(true);
          observer.onCompleted();
        } else {
          _log.debug(`checking if limit is ok with ${_this.limitCheckSubscriber}`);
          const topic = `limit-check-response (${_this.limitCheckId++})`;
          let limitCheckResponse:(msg:any) => void = (msg) => {
            _log.debug(`${_this.limitCheckSubscriber} limit check response was ${msg}`);
            observer.onNext(msg.result);
            observer.onCompleted();
          };

          fin.desktop.InterApplicationBus.subscribe(_this.limitCheckSubscriber, topic, limitCheckResponse);

          fin.desktop.InterApplicationBus.send(_this.limitCheckSubscriber, REQUEST_LIMIT_CHECK_TOPIC, {
            id: _this.limitCheckId,
            responseTopic: topic,
            tradedCurrencyPair: tradedCurrencyPair,
            notional: notional,
            rate: executablePrice
          });

          disposables.add(Rx.Disposable.create(() => {
            fin.desktop.InterApplicationBus.unsubscribe(_this.limitCheckSubscriber, topic, limitCheckResponse);
          }));
        }
        return disposables;
      });
  }

  get _currentWindow() {
    return fin.desktop.Window.getCurrent();
  }

  displayCurrencyChart(symbol){
    let chartIqAppId = 'ChartIQ';
    fin.desktop.System.getAllApplications((apps) => {
      let chartIqApp = _.find(apps, ((app) => {
        return app.isRunning && app.uuid === chartIqAppId;
      }));
      if(chartIqApp) {
        this._refreshCurrencyChart(symbol);
      } else {
        this._launchCurrencyChart(symbol);
      }
    });
  }

  /**
   * Initialize limit checker
   * @private
   */
  _initializeLimitChecker() {
    fin.desktop.main(() => {
      fin.desktop.InterApplicationBus.addSubscribeListener(function(uuid, topic) {
        if (topic === REQUEST_LIMIT_CHECK_TOPIC) {
          _log.info(`${uuid} has subscribed as a limit checker`);
          // There will only be one. If there are more, last subscriber will be used
          this.limitCheckSubscriber = uuid;
        }
      }.bind(this));
      fin.desktop.InterApplicationBus.addUnsubscribeListener(function(uuid, topic) {
        if (topic === REQUEST_LIMIT_CHECK_TOPIC) {
          _log.info(`${uuid} has unsubscribed as a limit checker`);
          this.limitCheckSubscriber = null;
        }
      }.bind(this));
    });
  }

  _refreshCurrencyChart(symbol){
    let interval = 5;
    let chartIqAppId = 'ChartIQ';
    fin.desktop.InterApplicationBus.publish('chartiq:main:change_symbol', { symbol: symbol, interval: interval });
  }

  _launchCurrencyChart(symbol){
    let interval = 5;
    let chartIqAppId = 'ChartIQ';
    let url = `http://openfin.chartiq.com/0.5/chartiq-shim.html?symbol=${symbol}&period=${interval}`;
    let name = `chartiq_${(new Date()).getTime()}`;
    const applicationIcon = 'http://openfin.chartiq.com/0.5/img/openfin-logo.png';
    let app = new fin.desktop.Application({
      uuid: chartIqAppId,
      url: url,
      name: name,
      applicationIcon: applicationIcon,
      mainWindowOptions:{
        autoShow: false
      }
    }, function(){
      app.run();
    });
  }

  openTradeNotification(trade:Trade): void {
    if (!this.isRunningInOpenFin) return;

    let tradeNotification = new TradeNotification(trade);

    let notification = new fin.desktop.Notification({
      url: '/notification.html',
      message: tradeNotification,
      onMessage: () => {
        this.maximise();
      }
    });
  }
}
<|endoftext|>


<commit_before>'use strict';
const util = require('../lib/test-utils.js');

describe('unexpected failures when calling fs functions (e.g. writeFile) with empty options object, issue773', function() {
  beforeEach(util.setup);
  afterEach(util.cleanup);

  it('should call fs.writeFile with an empty options object', function(done) {
    const fs = util.fs();
    fs.writeFile('/a', 'trololol', {}, (err) => {
      if (err) {
        done(err);
        return;
      }
      done();
    });
  });
});
<commit_msg>test: simplify regression test for issue773<commit_after>'use strict';
const util = require('../lib/test-utils.js');

describe('unexpected failures when calling fs functions (e.g. writeFile) with empty options object, issue773', function() {
  beforeEach(util.setup);
  afterEach(util.cleanup);

  it('should call fs.writeFile with an empty options object', function(done) {
    const fs = util.fs();
    fs.writeFile('/a', 'trololol', {}, done);
  });
});
<|endoftext|>