<commit_before>import extraConfigWhitelist from './extraConfigWhitelist';

/**
 * The config keys to whitelist, the keys that can be overridden.
 * Currently we can only whitelist the first part of the properties, like
 * 'p2p.enabled' we whitelist all p2p options.
 * The whitelist is used only for config.js.
 *
 * @type Array
 */
export default [
    '_desktopSharingSourceDevice',
    '_peerConnStatusOutOfLastNTimeout',
    '_peerConnStatusRtcMuteTimeout',
    'abTesting',
    'analytics.disabled',
    'audioLevelsInterval',
    'apiLogLevels',
    'avgRtpStatsN',

    /**
     * The display name of the CallKit call representing the conference/meeting
     * associated with this config.js including while the call is ongoing in the
     * UI presented by CallKit and in the system-wide call history. The property
     * is meant for use cases in which the room name is not desirable as a
     * display name for CallKit purposes and the desired display name is not
     * provided in the form of a JWT callee. As the value is associated with a
     * conference/meeting, the value makes sense not as a deployment-wide
     * configuration, only as a runtime configuration override/overwrite
     * provided by, for example, Jitsi Meet SDK for iOS.
     *
     * @type string
     */
    'callDisplayName',
    'callFlowsEnabled',

    /**
     * The handle
     * ({@link https://developer.apple.com/documentation/callkit/cxhandle}) of
     * the CallKit call representing the conference/meeting associated with this
     * config.js. The property is meant for use cases in which the room URL is
     * not desirable as the handle for CallKit purposes. As the value is
     * associated with a conference/meeting, the value makes sense not as a
     * deployment-wide configuration, only as a runtime configuration
     * override/overwrite provided by, for example, Jitsi Meet SDK for iOS.
     *
     * @type string
     */
    'callHandle',
    'callStatsConfIDNamespace',
    'callStatsID',
    'callStatsSecret',

    /**
     * The UUID of the CallKit call representing the conference/meeting
     * associated with this config.js. The property is meant for use cases in
     * which Jitsi Meet is to work with a CallKit call created outside of Jitsi
     * Meet and to be adopted by Jitsi Meet such as, for example, an incoming
     * and/or outgoing CallKit call created by Jitsi Meet SDK for iOS
     * clients/consumers prior to giving control to Jitsi Meet. As the value is
     * associated with a conference/meeting, the value makes sense not as a
     * deployment-wide configuration, only as a runtime configuration
     * override/overwrite provided by, for example, Jitsi Meet SDK for iOS.
     *
     * @type string
     */
    'callUUID',

    'channelLastN',
    'constraints',
    'brandingRoomAlias',
    'debug',
    'debugAudioLevels',
    'defaultLanguage',
    'desktopSharingFrameRate',
    'desktopSharingSources',
    'disable1On1Mode',
    'disableAEC',
    'disableAGC',
    'disableAP',
    'disableAudioLevels',
    'disableDeepLinking',
    'disableH264',
    'disableHPF',
    'disableInviteFunctions',
    'disableLocalVideoFlip',
    'disableNS',
    'disableProfile',
    'disableRemoteControl',
    'disableRemoteMute',
    'disableRtx',
    'disableSimulcast',
    'disableSuspendVideo',
    'disableThirdPartyRequests',
    'displayJids',
    'doNotStoreRoom',
    'e2eping',
    'enableDisplayNameInStats',
    'enableEmailInStats',
    'enableIceRestart',
    'enableInsecureRoomNameWarning',
    'enableLayerSuspension',
    'enableLipSync',
    'enableOpusRed',
    'enableRemb',
    'enableScreenshotCapture',
    'enableTalkWhileMuted',
    'enableNoAudioDetection',
    'enableNoisyMicDetection',
    'enableTcc',
    'enableAutomaticUrlCopy',
    'etherpad_base',
    'failICE',
    'feedbackPercentage',
    'fileRecordingsEnabled',
    'firefox_fake_device',
    'forceJVB121Ratio',
    'gatherStats',
    'googleApiApplicationClientID',
    'hiddenDomain',
    'hideLobbyButton',
    'hosts',
    'iAmRecorder',
    'iAmSipGateway',
    'iceTransportPolicy',
    'ignoreStartMuted',
    'liveStreamingEnabled',
    'localRecording',
    'maxFullResolutionParticipants',
    'minParticipants',
    'openBridgeChannel',
    'opusMaxAverageBitrate',
    'p2p',
    'pcStatsInterval',
    'preferH264',
    'prejoinPageEnabled',
    'requireDisplayName',
    'remoteVideoMenu',
    'roomPasswordNumberOfDigits',
    'resolution',
    'startAudioMuted',
    'startAudioOnly',
    'startBitrate',
    'startScreenSharing',
    'startSilent',
    'startVideoMuted',
    'startWithAudioMuted',
    'startWithVideoMuted',
    'stereo',
    'subject',
    'testing',
    'useTurnUdp',
    'videoQuality.persist',
    'webrtcIceTcpDisable',
    'webrtcIceUdpDisable'
].concat(extraConfigWhitelist);
<commit_msg>ref: Remove unused option from whitelist.<commit_after>import extraConfigWhitelist from './extraConfigWhitelist';

/**
 * The config keys to whitelist, the keys that can be overridden.
 * Currently we can only whitelist the first part of the properties, like
 * 'p2p.enabled' we whitelist all p2p options.
 * The whitelist is used only for config.js.
 *
 * @type Array
 */
export default [
    '_desktopSharingSourceDevice',
    '_peerConnStatusOutOfLastNTimeout',
    '_peerConnStatusRtcMuteTimeout',
    'abTesting',
    'analytics.disabled',
    'audioLevelsInterval',
    'apiLogLevels',
    'avgRtpStatsN',

    /**
     * The display name of the CallKit call representing the conference/meeting
     * associated with this config.js including while the call is ongoing in the
     * UI presented by CallKit and in the system-wide call history. The property
     * is meant for use cases in which the room name is not desirable as a
     * display name for CallKit purposes and the desired display name is not
     * provided in the form of a JWT callee. As the value is associated with a
     * conference/meeting, the value makes sense not as a deployment-wide
     * configuration, only as a runtime configuration override/overwrite
     * provided by, for example, Jitsi Meet SDK for iOS.
     *
     * @type string
     */
    'callDisplayName',
    'callFlowsEnabled',

    /**
     * The handle
     * ({@link https://developer.apple.com/documentation/callkit/cxhandle}) of
     * the CallKit call representing the conference/meeting associated with this
     * config.js. The property is meant for use cases in which the room URL is
     * not desirable as the handle for CallKit purposes. As the value is
     * associated with a conference/meeting, the value makes sense not as a
     * deployment-wide configuration, only as a runtime configuration
     * override/overwrite provided by, for example, Jitsi Meet SDK for iOS.
     *
     * @type string
     */
    'callHandle',
    'callStatsConfIDNamespace',
    'callStatsID',
    'callStatsSecret',

    /**
     * The UUID of the CallKit call representing the conference/meeting
     * associated with this config.js. The property is meant for use cases in
     * which Jitsi Meet is to work with a CallKit call created outside of Jitsi
     * Meet and to be adopted by Jitsi Meet such as, for example, an incoming
     * and/or outgoing CallKit call created by Jitsi Meet SDK for iOS
     * clients/consumers prior to giving control to Jitsi Meet. As the value is
     * associated with a conference/meeting, the value makes sense not as a
     * deployment-wide configuration, only as a runtime configuration
     * override/overwrite provided by, for example, Jitsi Meet SDK for iOS.
     *
     * @type string
     */
    'callUUID',

    'channelLastN',
    'constraints',
    'brandingRoomAlias',
    'debug',
    'debugAudioLevels',
    'defaultLanguage',
    'desktopSharingFrameRate',
    'desktopSharingSources',
    'disable1On1Mode',
    'disableAEC',
    'disableAGC',
    'disableAP',
    'disableAudioLevels',
    'disableDeepLinking',
    'disableH264',
    'disableHPF',
    'disableInviteFunctions',
    'disableLocalVideoFlip',
    'disableNS',
    'disableProfile',
    'disableRemoteControl',
    'disableRemoteMute',
    'disableRtx',
    'disableSimulcast',
    'disableThirdPartyRequests',
    'displayJids',
    'doNotStoreRoom',
    'e2eping',
    'enableDisplayNameInStats',
    'enableEmailInStats',
    'enableIceRestart',
    'enableInsecureRoomNameWarning',
    'enableLayerSuspension',
    'enableLipSync',
    'enableOpusRed',
    'enableRemb',
    'enableScreenshotCapture',
    'enableTalkWhileMuted',
    'enableNoAudioDetection',
    'enableNoisyMicDetection',
    'enableTcc',
    'enableAutomaticUrlCopy',
    'etherpad_base',
    'failICE',
    'feedbackPercentage',
    'fileRecordingsEnabled',
    'firefox_fake_device',
    'forceJVB121Ratio',
    'gatherStats',
    'googleApiApplicationClientID',
    'hiddenDomain',
    'hideLobbyButton',
    'hosts',
    'iAmRecorder',
    'iAmSipGateway',
    'iceTransportPolicy',
    'ignoreStartMuted',
    'liveStreamingEnabled',
    'localRecording',
    'maxFullResolutionParticipants',
    'minParticipants',
    'openBridgeChannel',
    'opusMaxAverageBitrate',
    'p2p',
    'pcStatsInterval',
    'preferH264',
    'prejoinPageEnabled',
    'requireDisplayName',
    'remoteVideoMenu',
    'roomPasswordNumberOfDigits',
    'resolution',
    'startAudioMuted',
    'startAudioOnly',
    'startBitrate',
    'startScreenSharing',
    'startSilent',
    'startVideoMuted',
    'startWithAudioMuted',
    'startWithVideoMuted',
    'stereo',
    'subject',
    'testing',
    'useTurnUdp',
    'videoQuality.persist',
    'webrtcIceTcpDisable',
    'webrtcIceUdpDisable'
].concat(extraConfigWhitelist);
<|endoftext|>


<commit_before>function hohoho(context) {

  //If DOM is given
  if (typeof context === "object") {
    //Check if DOM is already loaded or if we need an EventListener
    if (document.readyState === "complete") {
      insertHohoho();
    } else {
      window.addEventListener("onload", insertHohoho, false);
    }
  }
  //If String is given
  else if (typeof context === "string") {
    return hohohoify(context);
  }

  //Main Replacing Function
  function hohohoify(str) {
    return str.replace(
      //Regex for Finding instances of "Hi", "Hey" and "Hello"
      RegExp("(Hi|Hey|Hello)(?=[^>]*(<?))", "g"),
      "Ho Ho Ho"
    );
  }

  //If HoHoHo needs to be pasted to DOM
  function insertHohoho() {
    context.innerHTML = hohohoify(context.innerHTML);
  }
}
<commit_msg>added prototype<commit_after>function hohoho(context) {

  //If DOM is given
  if (typeof context === "object") {
    //Check if DOM is already loaded or if we need an EventListener
    if (document.readyState === "complete") {
      insertHohoho();
    } else {
      window.addEventListener("onload", insertHohoho, false);
    }
  }
  //If String is given
  else if (typeof context === "string") {
    return hohohoify(context);
  }

  //Main Replacing Function
  function hohohoify(str) {
    return str.replace(
      //Regex for Finding instances of "Hi", "Hey" and "Hello"
      RegExp("(Hi|Hey|Hello)(?=[^>]*(<?))", "g"),
      "Ho Ho Ho"
    );
  }

  //If HoHoHo needs to be pasted to DOM
  function insertHohoho() {
    context.innerHTML = hohohoify(context.innerHTML);
  }
}

anything.prototype.hohoho=hohoho;<|endoftext|>


<commit_before>/*
 * Copyright (c)  2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

(function() {

    var wso2gadgets = window.wso2gadgets || {};
    window.wso2gadgets = wso2gadgets;

    //Collection of view defintions
    var views = {};

    //Reference to currenr view defintion
    var currentView;

    //Reference to the vizg object of currentView object
    var chart;

    var canvas;

    var MODE_REDRAW = "redraw";
    var MODE_APPEND = "append";

    /*
     * Initialise the wso2gadgets object by passing the canvas element and view definitions.
     * View definitions can be an array or a single view definition object
     * Optionally default view can be passed (view that should be loaded first)
     * E.g wso2gadgets.init("#canvas",views,"chart-0");
     */
    wso2gadgets.init = function(el, configs, defaultView) {
        canvas = el;
        if (configs.constructor === Array) {
            configs.forEach(function(view) {
                views[view.id] = view;
            });
        } else if (typeof configs === 'object') {
            views[configs.id] = configs;
        }
        if (defaultView) {
            wso2gadgets.load(defaultView);
        }
    };

    wso2gadgets.load = function(id, context) {
        currentView = views[id];
        if (!currentView) {
            throw new Error("View with specified id [" + id + "] does not exist.");
        }
        //load data into current view using view's data configuration
        if (currentView.data && typeof currentView.data === "function") {
            currentView.data(context);
        }
        //initialize inter-gadget subscriptions
        // gadgets.HubSettings.onConnect = function() {
        //     if (currentView.subscriptions) {
        //         currentView.subscriptions.forEach(function(subscription) {
        //             gadgets.Hub.subscribe(subscription.topic, subscription.callback);
        //         });
        //     }
        // };
        console.log("View [" + id + "] has been loaded.");
    };

    /*
    * This method needs to be called by interested parties with necessary data in their hands.
    * data should be in VizGrammar compatible format
    * drawMode redraw || append. 'redraw' forces the chart to be redrawn with new data while 'append' just adds the new entries in to the chart.
    * Usually in realtime chart, this method needs to be called with drawMode='append'
    */
    wso2gadgets.onDataReady = function(data, drawMode) {
        try {
            if (data.length == 0) {
                $(canvas).html("No records found.");
                return;
            }
            //setting the data for the underlying VizGrammar chart
            currentView.schema[0].data = data;
            //calling the draw function on the current view object
            if (drawMode) {
                if(drawMode === MODE_REDRAW) {
                    wso2gadgets.draw(currentView);
                } else if(drawMode === MODE_APPEND) {
                    wso2gadgets.update(data);
                }
            } else {
                wso2gadgets.draw(currentView);  //if the drawMode parameter has been specified, use it, otherwise just redraw
            }
        } catch (e) {
            wso2gadgets.onError(e);
        }
    };

    wso2gadgets.draw = function(view) {
        if (!view.chartConfig.width) {
            view.chartConfig.width = $('body').width();
        }
        if (!view.chartConfig.height) {
            view.chartConfig.height = $('body').height();
        }
        $(canvas).empty();
        chart = new vizg(view.schema, view.chartConfig);
        if (view.callbacks && view.callbacks.length > 0) {
            chart.draw(canvas, view.callbacks);
        } else {
            chart.draw(canvas);
        }
    };

    wso2gadgets.update = function(data) {
        if (chart) {
            console.log(data);
            chart.insert(data);
        } else {
            wso2gadgets.draw(currentView);  //if this is the very first time we render the chart. E.g CEP usecase
        }
    };

    wso2gadgets.onError = function(e) {
        console.error(e);
        $(canvas).html("An error occurred while rendering the gadget.");
    };

})();<commit_msg>fixing preview rendering issue<commit_after>/*
 * Copyright (c)  2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

(function() {

    var wso2gadgets = window.wso2gadgets || {};
    window.wso2gadgets = wso2gadgets;

    //Collection of view defintions
    var views = {};

    //Reference to currenr view defintion
    var currentView;

    //Reference to the vizg object of currentView object
    var chart;

    var canvas;

    var MODE_REDRAW = "redraw";
    var MODE_APPEND = "append";

    /*
     * Initialise the wso2gadgets object by passing the canvas element and view definitions.
     * View definitions can be an array or a single view definition object
     * Optionally default view can be passed (view that should be loaded first)
     * E.g wso2gadgets.init("#canvas",views,"chart-0");
     */
    wso2gadgets.init = function(el, configs, defaultView) {
        canvas = el;
        if (configs.constructor === Array) {
            configs.forEach(function(view) {
                views[view.id] = view;
            });
        } else if (typeof configs === 'object') {
            views[configs.id] = configs;
        }
        if (defaultView) {
            wso2gadgets.load(defaultView);
        }
    };

    wso2gadgets.load = function(id, context) {
        currentView = views[id];
        if (!currentView) {
            throw new Error("View with specified id [" + id + "] does not exist.");
        }
        //load data into current view using view's data configuration
        if (currentView.data && typeof currentView.data === "function") {
            currentView.data(context);
        }
        //initialize inter-gadget subscriptions
        // gadgets.HubSettings.onConnect = function() {
        //     if (currentView.subscriptions) {
        //         currentView.subscriptions.forEach(function(subscription) {
        //             gadgets.Hub.subscribe(subscription.topic, subscription.callback);
        //         });
        //     }
        // };
    };

    /*
    * This method needs to be called by interested parties with necessary data in their hands.
    * data should be in VizGrammar compatible format
    * drawMode redraw || append. 'redraw' forces the chart to be redrawn with new data while 'append' just adds the new entries in to the chart.
    * Usually in realtime chart, this method needs to be called with drawMode='append'
    */
    wso2gadgets.onDataReady = function(data, drawMode) {
        try {
            if (data.length == 0) {
                $(canvas).html();
                return;
            }
            //setting the data for the underlying VizGrammar chart
            currentView.schema[0].data = data;
            //calling the draw function on the current view object
            if (drawMode) {
                if(drawMode === MODE_REDRAW) {
                    wso2gadgets.draw(currentView);
                } else if(drawMode === MODE_APPEND) {
                    wso2gadgets.update(data);
                }
            } else {
                wso2gadgets.draw(currentView);  //if the drawMode parameter has been specified, use it, otherwise just redraw
            }
        } catch (e) {
            wso2gadgets.onError(e);
        }
    };

    wso2gadgets.draw = function(view) {
        if (!view.chartConfig.width) {
            view.chartConfig.width = $('body').width();
        }
        if (!view.chartConfig.height) {
            view.chartConfig.height = $('body').height();
        }
        $(canvas).empty();
        chart = new vizg(view.schema, view.chartConfig);
        if (view.callbacks && view.callbacks.length > 0) {
            chart.draw(canvas, view.callbacks);
        } else {
            chart.draw(canvas);
        }
    };

    wso2gadgets.update = function(data) {
        if (chart) {
            chart.insert(data);
        } else {
            wso2gadgets.draw(currentView);  //if this is the very first time we render the chart. E.g CEP usecase
        }
    };

    wso2gadgets.onError = function(e) {
        $(canvas).html();
    };

})();<|endoftext|>


<commit_before>/* global angular, useOfflineBuffer */
'use strict';

function UserSessionService(base64, HttpBasicAuthenticationService, LocalStorageService, SessionStorageService) {
  var swapTokenBufferTime = 10*60*1000; // 10 minutes in milliseconds
  var latestModified = {};
  var itemsSynchronize = {};
  var ownerPrefix = 'my'; // default owner
  var offlineBufferEnabled = (typeof useOfflineBuffer !== 'undefined') ? useOfflineBuffer: false;

  function setOwnerPrefix(owner) {
    ownerPrefix = owner;
  }

  return {
    isAuthenticated: function() {
      return SessionStorageService.getUserUUID() || LocalStorageService.getUserUUID();
    },
    isAuthenticateValid: function() {
      var authenticateCurrentReferenceTime, authenticateValidTime;

      authenticateValidTime = SessionStorageService.getExpires() || LocalStorageService.getExpires();
      authenticateCurrentReferenceTime = Date.now() + swapTokenBufferTime;

      // If authentication valid, refresh session storage and encoded credentials for
      // HTTP Authorization header.
      if (authenticateValidTime > authenticateCurrentReferenceTime) {
        if (!SessionStorageService.getUserUUID()) {
          this.setUserSessionStorageData();
        } else if (!this.getCredentials()) {
          this.setEncodedCredentials(SessionStorageService.getHttpAuthorizationHeader());
        }
        return true;
      }
    },
    isAuthenticateReplaceable: function() {
      var authenticateCurrentReferenceTime, authenticateValidTime;

      if (LocalStorageService.getReplaceable()) {
        authenticateValidTime = LocalStorageService.getReplaceable();
        authenticateCurrentReferenceTime = Date.now() + swapTokenBufferTime;
        if (authenticateValidTime > authenticateCurrentReferenceTime) {
          return true;
        }
      }
    },
    isOfflineEnabled: function() {
      return offlineBufferEnabled;
    },
    clearUser: function() {
      SessionStorageService.clearUser();
      LocalStorageService.clearUser();
      latestModified = {};
      itemsSynchronize = {};
    },

    // owner
    setCollectivePrefix: function() {
      setOwnerPrefix('collective' + '/' + SessionStorageService.getActiveUUID());
    },
    setMyPrefix: function() {
      setOwnerPrefix('my');
    },
    getOwnerPrefix: function() {
      return ownerPrefix;
    },

    // Web storage setters
    setAuthenticateInformation: function(authenticateResponse) {
      var authExpiresDelta = Date.now() - authenticateResponse.authenticated;
      this.setCredentials('token', authenticateResponse.token);

      SessionStorageService.setActiveUUID(authenticateResponse.userUUID);
      SessionStorageService.setCollectives(authenticateResponse.collectives);
      SessionStorageService.setExpires(authenticateResponse.expires - authExpiresDelta);
      SessionStorageService.setHttpAuthorizationHeader(this.getCredentials());
      SessionStorageService.setUserType(authenticateResponse.userType);
      SessionStorageService.setUserUUID(authenticateResponse.userUUID);

      if (authenticateResponse.replaceable) {
        LocalStorageService.setExpires(authenticateResponse.expires - authExpiresDelta);
        LocalStorageService.setCollectives(authenticateResponse.collectives);
        LocalStorageService.setHttpAuthorizationHeader(this.getCredentials());
        LocalStorageService.setReplaceable(authenticateResponse.replaceable - authExpiresDelta);
        LocalStorageService.setUserType(authenticateResponse.userType);
        LocalStorageService.setUserUUID(authenticateResponse.userUUID);
      }
    },
    setUserSessionStorageData: function() {
      SessionStorageService.setActiveUUID(LocalStorageService.getUserUUID());
      SessionStorageService.setCollectives(LocalStorageService.getCollectives());
      SessionStorageService.setExpires(LocalStorageService.getExpires());
      SessionStorageService.setHttpAuthorizationHeader(LocalStorageService.getHttpAuthorizationHeader());
      SessionStorageService.setUserType(LocalStorageService.getUserType());
      SessionStorageService.setUserUUID(LocalStorageService.getUserUUID());

      this.setEncodedCredentials(SessionStorageService.getHttpAuthorizationHeader());
    },
    setActiveUUID: function(uuid) {
      SessionStorageService.setActiveUUID(uuid);
    },
    setCredentials: function(username, password) {
      this.setEncodedCredentials(base64.encode(username + ':' + password));
    },
    setEncodedCredentials: function(userpass) {
      HttpBasicAuthenticationService.setCredentials(userpass);
    },
    setEncodedCredentialsFromLocalStorage: function() {
      this.setEncodedCredentials(LocalStorageService.getHttpAuthorizationHeader());
    },
    setLatestModified: function(modified, ownerUUID) {
      // Only set if given value is larger than set value
      if (!latestModified[ownerUUID] || latestModified[ownerUUID] < modified){
        latestModified[ownerUUID] = modified;
      }
    },
    setItemsSynchronizing: function(ownerUUID) {
      if (!itemsSynchronize[ownerUUID]) {
        itemsSynchronize[ownerUUID] = {};
      }
      itemsSynchronize[ownerUUID].itemsSynchronizing = true;
    },
    setItemsSynchronized: function(ownerUUID) {
      if (!itemsSynchronize[ownerUUID]) {
        itemsSynchronize[ownerUUID] = {};
      }
      itemsSynchronize[ownerUUID].itemsSynchronizing = false;
      itemsSynchronize[ownerUUID].itemsSynchronized = Date.now();
    },

    // Web storage getters
    getActiveUUID: function() {
      return SessionStorageService.getActiveUUID();
    },
    getCollectives: function() {
      return SessionStorageService.getCollectives();
    },
    getCredentials: function() {
      return HttpBasicAuthenticationService.getCredentials();
    },
    getUserUUID: function() {
      return SessionStorageService.getUserUUID();
    },
    getLatestModified: function(ownerUUID) {
      return latestModified[ownerUUID];
    },
    isItemsSynchronizing: function(ownerUUID) {
      if (itemsSynchronize[ownerUUID]) {
        return itemsSynchronize[ownerUUID].itemsSynchronizing;
      }
    },
    getItemsSynchronized: function(ownerUUID) {
      return (itemsSynchronize[ownerUUID]) ? itemsSynchronize[ownerUUID].itemsSynchronized : undefined;
    },
    getRememberByDefault: function() {
      return offlineBufferEnabled;
    }
  };
}
UserSessionService.$inject = ['base64', 'HttpBasicAuthenticationService', 'LocalStorageService', 'SessionStorageService'];
angular.module('em.services').factory('UserSessionService', UserSessionService);
<commit_msg>Sync session storage with local storage before getting items from session storage. Refactored few UsesSessionStorage public functions to private.<commit_after>/* global angular, useOfflineBuffer */
'use strict';

function UserSessionService(base64, HttpBasicAuthenticationService, LocalStorageService, SessionStorageService) {
  var swapTokenBufferTime = 10*60*1000; // 10 minutes in milliseconds
  var latestModified = {};
  var itemsSynchronize = {};
  var ownerPrefix = 'my'; // default owner
  var offlineBufferEnabled = (typeof useOfflineBuffer !== 'undefined') ? useOfflineBuffer: false;

  function setOwnerPrefix(owner) {
    ownerPrefix = owner;
  }

  // Sync session storage with local storage.
  function syncWebStorages() {
    if (LocalStorageService.getExpires() && SessionStorageService.getExpires() !== LocalStorageService.getExpires()) {
      setUserSessionStorageData();
    }
  }

  function setUserSessionStorageData() {
    SessionStorageService.setActiveUUID(LocalStorageService.getUserUUID());
    SessionStorageService.setCollectives(LocalStorageService.getCollectives());
    SessionStorageService.setExpires(LocalStorageService.getExpires());
    SessionStorageService.setHttpAuthorizationHeader(LocalStorageService.getHttpAuthorizationHeader());
    SessionStorageService.setUserType(LocalStorageService.getUserType());
    SessionStorageService.setUserUUID(LocalStorageService.getUserUUID());

    setEncodedCredentials(SessionStorageService.getHttpAuthorizationHeader());
  }
  function setEncodedCredentials(userpass) {
    HttpBasicAuthenticationService.setCredentials(userpass);
  }

  return {
    isAuthenticated: function() {
      return SessionStorageService.getUserUUID() || LocalStorageService.getUserUUID();
    },
    isAuthenticateValid: function() {
      var authenticateCurrentReferenceTime, authenticateValidTime;

      authenticateValidTime = SessionStorageService.getExpires() || LocalStorageService.getExpires();
      authenticateCurrentReferenceTime = Date.now() + swapTokenBufferTime;

      // If authentication valid, refresh session storage and encoded credentials for
      // HTTP Authorization header.
      if (authenticateValidTime > authenticateCurrentReferenceTime) {
        if (!SessionStorageService.getUserUUID()) {
          setUserSessionStorageData();
        } else if (!this.getCredentials()) {
          setEncodedCredentials(SessionStorageService.getHttpAuthorizationHeader());
        }
        return true;
      }
    },
    isAuthenticateReplaceable: function() {
      var authenticateCurrentReferenceTime, authenticateValidTime;

      if (LocalStorageService.getReplaceable()) {
        authenticateValidTime = LocalStorageService.getReplaceable();
        authenticateCurrentReferenceTime = Date.now() + swapTokenBufferTime;
        if (authenticateValidTime > authenticateCurrentReferenceTime) {
          return true;
        }
      }
    },
    isOfflineEnabled: function() {
      return offlineBufferEnabled;
    },
    clearUser: function() {
      SessionStorageService.clearUser();
      LocalStorageService.clearUser();
      latestModified = {};
      itemsSynchronize = {};
    },

    // owner
    setCollectivePrefix: function() {
      setOwnerPrefix('collective' + '/' + SessionStorageService.getActiveUUID());
    },
    setMyPrefix: function() {
      setOwnerPrefix('my');
    },
    getOwnerPrefix: function() {
      return ownerPrefix;
    },

    // Web storage setters
    setAuthenticateInformation: function(authenticateResponse) {
      var authExpiresDelta = Date.now() - authenticateResponse.authenticated;
      this.setCredentials('token', authenticateResponse.token);

      SessionStorageService.setActiveUUID(authenticateResponse.userUUID);
      SessionStorageService.setCollectives(authenticateResponse.collectives);
      SessionStorageService.setExpires(authenticateResponse.expires - authExpiresDelta);
      SessionStorageService.setHttpAuthorizationHeader(this.getCredentials());
      SessionStorageService.setUserType(authenticateResponse.userType);
      SessionStorageService.setUserUUID(authenticateResponse.userUUID);

      if (authenticateResponse.replaceable) {
        LocalStorageService.setExpires(authenticateResponse.expires - authExpiresDelta);
        LocalStorageService.setCollectives(authenticateResponse.collectives);
        LocalStorageService.setHttpAuthorizationHeader(this.getCredentials());
        LocalStorageService.setReplaceable(authenticateResponse.replaceable - authExpiresDelta);
        LocalStorageService.setUserType(authenticateResponse.userType);
        LocalStorageService.setUserUUID(authenticateResponse.userUUID);
      }
    },
    setActiveUUID: function(uuid) {
      SessionStorageService.setActiveUUID(uuid);
    },
    setCredentials: function(username, password) {
      setEncodedCredentials(base64.encode(username + ':' + password));
    },
    setEncodedCredentialsFromLocalStorage: function() {
      setEncodedCredentials(LocalStorageService.getHttpAuthorizationHeader());
    },
    setLatestModified: function(modified, ownerUUID) {
      // Only set if given value is larger than set value
      if (!latestModified[ownerUUID] || latestModified[ownerUUID] < modified){
        latestModified[ownerUUID] = modified;
      }
    },
    setItemsSynchronizing: function(ownerUUID) {
      if (!itemsSynchronize[ownerUUID]) {
        itemsSynchronize[ownerUUID] = {};
      }
      itemsSynchronize[ownerUUID].itemsSynchronizing = true;
    },
    setItemsSynchronized: function(ownerUUID) {
      if (!itemsSynchronize[ownerUUID]) {
        itemsSynchronize[ownerUUID] = {};
      }
      itemsSynchronize[ownerUUID].itemsSynchronizing = false;
      itemsSynchronize[ownerUUID].itemsSynchronized = Date.now();
    },

    // Web storage getters
    getActiveUUID: function() {
      syncWebStorages();
      return SessionStorageService.getActiveUUID();
    },
    getCollectives: function() {
      syncWebStorages();
      return SessionStorageService.getCollectives();
    },
    getCredentials: function() {
      syncWebStorages();
      return HttpBasicAuthenticationService.getCredentials();
    },
    getUserUUID: function() {
      syncWebStorages();
      return SessionStorageService.getUserUUID();
    },
    getLatestModified: function(ownerUUID) {
      return latestModified[ownerUUID];
    },
    isItemsSynchronizing: function(ownerUUID) {
      if (itemsSynchronize[ownerUUID]) {
        return itemsSynchronize[ownerUUID].itemsSynchronizing;
      }
    },
    getItemsSynchronized: function(ownerUUID) {
      return (itemsSynchronize[ownerUUID]) ? itemsSynchronize[ownerUUID].itemsSynchronized : undefined;
    },
    getRememberByDefault: function() {
      return offlineBufferEnabled;
    }
  };
}
UserSessionService.$inject = ['base64', 'HttpBasicAuthenticationService', 'LocalStorageService', 'SessionStorageService'];
angular.module('em.services').factory('UserSessionService', UserSessionService);
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2013 Anis Kadri
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/* jshint unused: vars */

var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;

var handlers = {
    'source-file':{
        install:function(obj, plugin, project, options) {
            if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
            if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));

            var dest = path.join(obj.targetDir, path.basename(obj.src));

            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
            }

            if (options && options.force) {
                copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
            } else {
                copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
            }
        },
        uninstall:function(obj, plugin, project, options) {
            var dest = path.join(obj.targetDir, path.basename(obj.src));

            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
            }

            deleteJava(project.projectDir, dest);
        }
    },
    'lib-file':{
        install:function(obj, plugin, project, options) {
            var dest = path.join('libs', path.basename(obj.src));
            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/libs', path.basename(obj.src));
            }
            copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
        },
        uninstall:function(obj, plugin, project, options) {
            var dest = path.join('libs', path.basename(obj.src));
            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/libs', path.basename(obj.src));
            }
            removeFile(project.projectDir, dest);
        }
    },
    'resource-file':{
        install:function(obj, plugin, project, options) {
            copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
        },
        uninstall:function(obj, plugin, project, options) {
            removeFile(project.projectDir, path.normalize(obj.target));
        }
    },
    'framework': {
        install:function(obj, plugin, project, options) {
            var src = obj.src;
            if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));

            events.emit('verbose', 'Installing Android library: ' + src);
            var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
            var subDir;

            if (obj.custom) {
                var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
                copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link));
                subDir = path.resolve(project.projectDir, subRelativeDir);
            } else {
                // WEEX_HOOK
                obj.type = 'gradleReference';
                //obj.type = 'sys';
                subDir = src;
            }

            if (obj.type == 'gradleReference') {
                project.addGradleReference(parentDir, subDir);
            } else if (obj.type == 'sys') {
                project.addSystemLibrary(parentDir, subDir);
            } else {
                project.addSubProject(parentDir, subDir);
            }
        },
        uninstall:function(obj, plugin, project, options) {
            var src = obj.src;
            if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));

            events.emit('verbose', 'Uninstalling Android library: ' + src);
            var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
            var subDir;

            if (obj.custom) {
                var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
                removeFile(project.projectDir, subRelativeDir);
                subDir = path.resolve(project.projectDir, subRelativeDir);
                // If it's the last framework in the plugin, remove the parent directory.
                var parDir = path.dirname(subDir);
                if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) {
                    fs.rmdirSync(parDir);
                }
            } else {
                obj.type = 'sys';
                subDir = src;
            }

            if (obj.type == 'gradleReference') {
                project.removeGradleReference(parentDir, subDir);
            } else if (obj.type == 'sys') {
                project.removeSystemLibrary(parentDir, subDir);
            } else {
                project.removeSubProject(parentDir, subDir);
            }
        }
    },
    asset:{
        install:function(obj, plugin, project, options) {
            if (!obj.src) {
                throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
            }
            if (!obj.target) {
                throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
            }

            copyFile(plugin.dir, obj.src, project.www, obj.target);
            if (options && options.usePlatformWww) {
                // CB-11022 copy file to both directories if usePlatformWww is specified
                copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
            }
        },
        uninstall:function(obj, plugin, project, options) {
            var target = obj.target || obj.src;

            if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));

            removeFileF(path.resolve(project.www, target));
            removeFileF(path.resolve(project.www, 'plugins', plugin.id));
            if (options && options.usePlatformWww) {
                // CB-11022 remove file from both directories if usePlatformWww is specified
                removeFileF(path.resolve(project.platformWww, target));
                removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
            }
        }
    },
    'js-module': {
        install: function (obj, plugin, project, options) {
            // Copy the plugin's files into the www directory.
            var moduleSource = path.resolve(plugin.dir, obj.src);
            var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src)));

            // Read in the file, prepend the cordova.define, and write it back out.
            var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
            if (moduleSource.match(/.*\.json$/)) {
                scriptContent = 'module.exports = ' + scriptContent;
            }
            scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';

            var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
            shell.mkdir('-p', path.dirname(wwwDest));
            fs.writeFileSync(wwwDest, scriptContent, 'utf-8');

            if (options && options.usePlatformWww) {
                // CB-11022 copy file to both directories if usePlatformWww is specified
                var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
                shell.mkdir('-p', path.dirname(platformWwwDest));
                fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
            }
        },
        uninstall: function (obj, plugin, project, options) {
            var pluginRelativePath = path.join('plugins', plugin.id, obj.src);
            removeFileAndParents(project.www, pluginRelativePath);
            if (options && options.usePlatformWww) {
                // CB-11022 remove file from both directories if usePlatformWww is specified
                removeFileAndParents(project.platformWww, pluginRelativePath);
            }
        }
    }
};

module.exports.getInstaller = function (type) {
    if (handlers[type] && handlers[type].install) {
        return handlers[type].install;
    }

    events.emit('verbose', '<' + type + '> is not supported for android plugins');
};

module.exports.getUninstaller = function(type) {
    if (handlers[type] && handlers[type].uninstall) {
        return handlers[type].uninstall;
    }

    events.emit('verbose', '<' + type + '> is not supported for android plugins');
};

function copyFile (plugin_dir, src, project_dir, dest, link) {
    src = path.resolve(plugin_dir, src);
    if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!');

    // check that src path is inside plugin directory
    var real_path = fs.realpathSync(src);
    var real_plugin_path = fs.realpathSync(plugin_dir);
    if (real_path.indexOf(real_plugin_path) !== 0)
        throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');

    dest = path.resolve(project_dir, dest);

    // check that dest path is located in project directory
    if (dest.indexOf(project_dir) !== 0)
        throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');

    shell.mkdir('-p', path.dirname(dest));
    if (link) {
        symlinkFileOrDirTree(src, dest);
    } else if (fs.statSync(src).isDirectory()) {
        // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
        shell.cp('-Rf', src+'/*', dest);
    } else {
        shell.cp('-f', src, dest);
    }
}

// Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
    var target_path = path.resolve(project_dir, dest);
    if (fs.existsSync(target_path))
        throw new CordovaError('"' + target_path + '" already exists!');

    copyFile(plugin_dir, src, project_dir, dest, !!link);
}

function symlinkFileOrDirTree(src, dest) {
    if (fs.existsSync(dest)) {
        shell.rm('-Rf', dest);
    }

    if (fs.statSync(src).isDirectory()) {
        shell.mkdir('-p', dest);
        fs.readdirSync(src).forEach(function(entry) {
            symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
        });
    }
    else {
        fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
    }
}

// checks if file exists and then deletes. Error if doesn't exist
function removeFile (project_dir, src) {
    var file = path.resolve(project_dir, src);
    shell.rm('-Rf', file);
}

// deletes file/directory without checking
function removeFileF (file) {
    shell.rm('-Rf', file);
}

// Sometimes we want to remove some java, and prune any unnecessary empty directories
function deleteJava (project_dir, destFile) {
    removeFileAndParents(project_dir, destFile, 'src');
}

function removeFileAndParents (baseDir, destFile, stopper) {
    stopper = stopper || '.';
    var file = path.resolve(baseDir, destFile);
    if (!fs.existsSync(file)) return;

    removeFileF(file);

    // check if directory is empty
    var curDir = path.dirname(file);

    while(curDir !== path.resolve(baseDir, stopper)) {
        if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
            fs.rmdirSync(curDir);
            curDir = path.resolve(curDir, '..');
        } else {
            // directory not empty...do nothing
            break;
        }
    }
}

function generateAttributeError(attribute, element, id) {
    return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
}
<commit_msg>add support for plugin remove<commit_after>/*
 *
 * Copyright 2013 Anis Kadri
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/* jshint unused: vars */

var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;

var handlers = {
    'source-file':{
        install:function(obj, plugin, project, options) {
            if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
            if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));

            var dest = path.join(obj.targetDir, path.basename(obj.src));

            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
            }

            if (options && options.force) {
                copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
            } else {
                copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
            }
        },
        uninstall:function(obj, plugin, project, options) {
            var dest = path.join(obj.targetDir, path.basename(obj.src));

            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
            }

            deleteJava(project.projectDir, dest);
        }
    },
    'lib-file':{
        install:function(obj, plugin, project, options) {
            var dest = path.join('libs', path.basename(obj.src));
            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/libs', path.basename(obj.src));
            }
            copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
        },
        uninstall:function(obj, plugin, project, options) {
            var dest = path.join('libs', path.basename(obj.src));
            if(options && options.android_studio === true) {
              dest = path.join('weexplugin/libs', path.basename(obj.src));
            }
            removeFile(project.projectDir, dest);
        }
    },
    'resource-file':{
        install:function(obj, plugin, project, options) {
            copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
        },
        uninstall:function(obj, plugin, project, options) {
            removeFile(project.projectDir, path.normalize(obj.target));
        }
    },
    'framework': {
        install:function(obj, plugin, project, options) {
            var src = obj.src;
            if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));

            events.emit('verbose', 'Installing Android library: ' + src);
            var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
            var subDir;

            if (obj.custom) {
                var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
                copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link));
                subDir = path.resolve(project.projectDir, subRelativeDir);
            } else {
                // WEEX_HOOK
                obj.type = 'gradleReference';
                //obj.type = 'sys';
                subDir = src;
            }

            if (obj.type == 'gradleReference') {
                project.addGradleReference(parentDir, subDir);
            } else if (obj.type == 'sys') {
                project.addSystemLibrary(parentDir, subDir);
            } else {
                project.addSubProject(parentDir, subDir);
            }
        },
        uninstall:function(obj, plugin, project, options) {
            var src = obj.src;
            if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));

            events.emit('verbose', 'Uninstalling Android library: ' + src);
            var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
            var subDir;

            if (obj.custom) {
                var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
                removeFile(project.projectDir, subRelativeDir);
                subDir = path.resolve(project.projectDir, subRelativeDir);
                // If it's the last framework in the plugin, remove the parent directory.
                var parDir = path.dirname(subDir);
                if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) {
                    fs.rmdirSync(parDir);
                }
            } else {
                // WEEX_HOOK
                obj.type = 'gradleReference';
                //obj.type = 'sys';
                subDir = src;
            }

            if (obj.type == 'gradleReference') {
                project.removeGradleReference(parentDir, subDir);
            } else if (obj.type == 'sys') {
                project.removeSystemLibrary(parentDir, subDir);
            } else {
                project.removeSubProject(parentDir, subDir);
            }
        }
    },
    asset:{
        install:function(obj, plugin, project, options) {
            if (!obj.src) {
                throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
            }
            if (!obj.target) {
                throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
            }

            copyFile(plugin.dir, obj.src, project.www, obj.target);
            if (options && options.usePlatformWww) {
                // CB-11022 copy file to both directories if usePlatformWww is specified
                copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
            }
        },
        uninstall:function(obj, plugin, project, options) {
            var target = obj.target || obj.src;

            if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));

            removeFileF(path.resolve(project.www, target));
            removeFileF(path.resolve(project.www, 'plugins', plugin.id));
            if (options && options.usePlatformWww) {
                // CB-11022 remove file from both directories if usePlatformWww is specified
                removeFileF(path.resolve(project.platformWww, target));
                removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
            }
        }
    },
    'js-module': {
        install: function (obj, plugin, project, options) {
            // Copy the plugin's files into the www directory.
            var moduleSource = path.resolve(plugin.dir, obj.src);
            var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src)));

            // Read in the file, prepend the cordova.define, and write it back out.
            var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
            if (moduleSource.match(/.*\.json$/)) {
                scriptContent = 'module.exports = ' + scriptContent;
            }
            scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';

            var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
            shell.mkdir('-p', path.dirname(wwwDest));
            fs.writeFileSync(wwwDest, scriptContent, 'utf-8');

            if (options && options.usePlatformWww) {
                // CB-11022 copy file to both directories if usePlatformWww is specified
                var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
                shell.mkdir('-p', path.dirname(platformWwwDest));
                fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
            }
        },
        uninstall: function (obj, plugin, project, options) {
            var pluginRelativePath = path.join('plugins', plugin.id, obj.src);
            removeFileAndParents(project.www, pluginRelativePath);
            if (options && options.usePlatformWww) {
                // CB-11022 remove file from both directories if usePlatformWww is specified
                removeFileAndParents(project.platformWww, pluginRelativePath);
            }
        }
    }
};

module.exports.getInstaller = function (type) {
    if (handlers[type] && handlers[type].install) {
        return handlers[type].install;
    }

    events.emit('verbose', '<' + type + '> is not supported for android plugins');
};

module.exports.getUninstaller = function(type) {
    if (handlers[type] && handlers[type].uninstall) {
        return handlers[type].uninstall;
    }

    events.emit('verbose', '<' + type + '> is not supported for android plugins');
};

function copyFile (plugin_dir, src, project_dir, dest, link) {
    src = path.resolve(plugin_dir, src);
    if (!fs.existsSync(src)) throw new CordovaError('"' + src + '" not found!');

    // check that src path is inside plugin directory
    var real_path = fs.realpathSync(src);
    var real_plugin_path = fs.realpathSync(plugin_dir);
    if (real_path.indexOf(real_plugin_path) !== 0)
        throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');

    dest = path.resolve(project_dir, dest);

    // check that dest path is located in project directory
    if (dest.indexOf(project_dir) !== 0)
        throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');

    shell.mkdir('-p', path.dirname(dest));
    if (link) {
        symlinkFileOrDirTree(src, dest);
    } else if (fs.statSync(src).isDirectory()) {
        // XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
        shell.cp('-Rf', src+'/*', dest);
    } else {
        shell.cp('-f', src, dest);
    }
}

// Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
    var target_path = path.resolve(project_dir, dest);
    if (fs.existsSync(target_path))
        throw new CordovaError('"' + target_path + '" already exists!');

    copyFile(plugin_dir, src, project_dir, dest, !!link);
}

function symlinkFileOrDirTree(src, dest) {
    if (fs.existsSync(dest)) {
        shell.rm('-Rf', dest);
    }

    if (fs.statSync(src).isDirectory()) {
        shell.mkdir('-p', dest);
        fs.readdirSync(src).forEach(function(entry) {
            symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
        });
    }
    else {
        fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
    }
}

// checks if file exists and then deletes. Error if doesn't exist
function removeFile (project_dir, src) {
    var file = path.resolve(project_dir, src);
    shell.rm('-Rf', file);
}

// deletes file/directory without checking
function removeFileF (file) {
    shell.rm('-Rf', file);
}

// Sometimes we want to remove some java, and prune any unnecessary empty directories
function deleteJava (project_dir, destFile) {
    removeFileAndParents(project_dir, destFile, 'src');
}

function removeFileAndParents (baseDir, destFile, stopper) {
    stopper = stopper || '.';
    var file = path.resolve(baseDir, destFile);
    if (!fs.existsSync(file)) return;

    removeFileF(file);

    // check if directory is empty
    var curDir = path.dirname(file);

    while(curDir !== path.resolve(baseDir, stopper)) {
        if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
            fs.rmdirSync(curDir);
            curDir = path.resolve(curDir, '..');
        } else {
            // directory not empty...do nothing
            break;
        }
    }
}

function generateAttributeError(attribute, element, id) {
    return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
}
<|endoftext|>


<commit_before>
  var http = require('http');
  var httpProxy = require('http-proxy');
  var socket_io = require('socket.io');
  var fs = require('fs');
  var url = require('url');
  var path = require('path');

  var fs_debug = false;
  var is_dev = true;

  var MIME_TYPE = {
    'css':  'text/css',
    'js':   'text/javascript',
    'html': 'text/html',
    'jpg':  'image/jpg',
    'png':  'image/png',
    'gif':  'image/gif'
  }

  var BASE_PATH = process.argv[2];
  var port = process.argv[3];

  if (isNaN(port))
    port = 0;

  //load proxy pathes
  var config = {};
  var rewriteRules = {};
  try {
    var data = fs.readFileSync(path.resolve(BASE_PATH, 'server.config'));
    config = JSON.parse(String(data));
    if (config.rewrite)
      rewriteRules = config.rewrite;

    if (Array.isArray(config.ignore))
    {
      config.ignore = config.ignore.reduce(function(result, p){
        result[path.resolve(BASE_PATH, p)] = true;
        return result;
      }, {});
      console.log('Ignore pathes:\n  ' + Object.keys(config.ignore).join('\n  '));
    }
    else
      delete config.ignore;
  } catch(e) {
    console.warn(e);
  }

  //proxy
  var proxy = new httpProxy.HttpProxy({
    target: {
      host: 'localhost',
      port: 80
    }
  })

  //create server
  var app = http.createServer(function(req, res){

    var location = url.parse(req.url, true, true);
    var host = req.headers.host;

    //proxy request if nececcary
    var re;
    var pathname = location.pathname.slice(1);
    for (var rule in rewriteRules)
    {
      re = new RegExp(rule);
      if (re.test(pathname))
      {
        console.log(re);
        proxy.proxyRequest(req, res);
        return;
      }
    }

    var filename = path.normalize(BASE_PATH + location.pathname);

    if (!path.existsSync(filename))
    {
      res.writeHead(404);
      res.end('File ' + filename + ' not found');
    }
    else
    {
      if (fs.statSync(filename).isDirectory())
      {
        if (path.existsSync(filename + '/index.html'))
          filename += '/index.html';
        else
          if (path.existsSync(filename + '/index.htm'))
            filename += '/index.htm';
          else
          {
            res.writeHead(404);
            res.end('Path ' + filename + ' is not file');
          }
      }

      var ext = path.extname(filename);

      fs.readFile(filename, function(err, data){
        if (err)
        {
          res.writeHead(500);
          res.end('Can\'t read file ' + filename + ', error: ' + err);
        }
        else
        {
          res.writeHead(200, {
            'Content-Type': MIME_TYPE[ext.slice(1)] || 'text/plain'
          });

          if (ext == '.html' || ext == '.htm')
          {
            fs.readFile(__dirname + '/client.js', function(err, clientFileData){
              if (!err)
              {
                data = String(data).replace(/<\/body>/, '');
                res.end(data);
              }
            });
          }
          else
          {
            res.end(data);
          }
        }
      });
    }
  });

  app.listen(port, function(){
    var port = app.address().port;
    console.log('Server is online, listen for http://localhost:' + port + '\nWatching changes for path: ' + BASE_PATH);
  });
  var io = socket_io.listen(app);

  //
  // Messaging
  //

  io.sockets.on('connection', function(socket){
    socket.on('saveFile', function(filename, content){
      console.log('save file', arguments);

      var fname = path.normalize(BASE_PATH + '/' + filename);
      var fnKey = normPath(fname);

      if (fsWatcher.isObserveFile(fnKey))
      {
        fs.writeFile(fname, content, function (err) {
          if (err)
          {
            socket.emit('fileSaveError', {
              filename: filename,
              message: err
            });
          }
          else
          {
            socket.emit('fileSaved', fnKey);
          }
        });
      }
      else
        socket.emit('fileSaveError', {
          filename: filename,
          message: 'file not observable'
        });
    });

    socket.on('createFile', function(filename, content){
      console.log('create file', arguments);

      var fname = BASE_PATH + '/' + filename;

      if (!path.existsSync(fname) && path.existsSync(path.dirname(fname)))
      {
        fs.writeFile(fname, '', function (err) {
          if (err)
          {
            socket.emit('error', {
              operation: 'create file',
              message: err
            });
          }
          else
          {
            socket.emit('fileSaved', filename);
          }
        });
      }
      else
        socket.emit('error', {
          operation: 'create file',
          message: 'bad filename'
        });
    });

    socket.on('readFile', function(filename, content){
      console.log('read file', arguments);

      fsWatcher.readFile(BASE_PATH + '/' + filename);
    });

    socket.on('observe', function(rel, fspath){
      fsWatcher.watch(BASE_PATH);
      socket.emit('observeReady', fsWatcher.getFiles(BASE_PATH + '/'));
    });
  });

  io.sockets.on('disconnect', function(socket){
    //socket.files.forEach(fsWatcher.removeContentObserver);
    delete socket.pathes;
  });

  function normPath(filename){
    return '/' + path.relative(BASE_PATH, path.resolve(BASE_PATH, filename)).replace(/\\/g, '/')
  }

  //
  // File System Watcher
  //

  var fsWatcher = (function(){
    var fileMap = {};
    var dirMap = {};

    function readFile(filename){
      if (path.extname(filename) == '.css' || path.extname(filename) == '.tmpl' || path.extname(filename) == '.txt' || path.extname(filename) == '.json')
      {
        fs.readFile(filename, 'utf8', function(err, data){
          console.log('READ FILE:', filename);
          if (!err)
          {
            console.log('key', path.relative(BASE_PATH, filename).replace(/\\/g, '/'));

            var fileInfo = fileMap[normPath(filename)];
            var newContent = String(data).replace(/\r\n?|\n\r?/g, '\n');

            var newFileInfo = {
              filename: filename,
              lastUpdate: fileInfo.mtime
            };

            if (newContent !== fileInfo.content)
            {
              fileInfo.content = newContent;
            }

            newFileInfo.content = newContent;

            updateCallback(newFileInfo);
          }
          else
            console.log('   \033[31merror of file read (' + filename + '): ' + err + ' \033[39m');
        });
      }
    }

    function updateStat(filename){
      fs.stat(filename, function(err, stats){
        var fnKey = normPath(filename);

        if (err)
        {
          console.log('updateStat error:', err);
        }
        else
        {
          var fileInfo = fileMap[fnKey];

          if (!fileMap[fnKey])
          {
            var fileType = stats.isDirectory() ? 'dir' : 'file';

            fileMap[fnKey] = {
              mtime: stats.mtime,
              type: fileType,
              listeners: [],
              content: null
            };

            // event!! new file
            if (filename != BASE_PATH)
            {
              createCallback(filename, fileType, stats.mtime);
              console.log(filename + ' - found');
            }

            if (fileType == 'dir')
            {
              console.log(filename, path.normalize(filename));
              if (!config.ignore || !config.ignore[path.normalize(filename)])
                lookup(filename);
            }
          }
          else
          {
            if (fileMap[fnKey].type == 'file' && stats.mtime - fileMap[fnKey].mtime)
            {
              fileInfo.mtime = stats.mtime;

              readFile(filename);
            }
          }
        }
      });
    }

    function lookup(path){
      fs.readdir(path, function(err, files){
        if (err)
          console.log('lookup error:', path, err);
        else
        {
          var filename;
          var dirInfo = dirMap[path];

          updateStat(path);

          if (dirInfo)
          {
            var dirFiles = dirInfo.files;
            for (var i = 0, file; file = dirFiles[i++];)
            {
              if (files.indexOf(file) == -1)
              {
                var filename = path + '/' + file;
                delete dirFiles[filename];
                delete fileMap[filename];

                // event!!
                deleteCallback(filename);
                if (fs_debug) console.log(filename + ' - missed'); // file lost
              }
            }
          }
          else
          {
            dirInfo = dirMap[path] = {
              path: path,
              watcher: fs.watch(path, function(event, filename){
                lookup(path);
              })
            };
          }

          dirInfo.files = files;

          if (files.length)
          {
            for (var file, i = 0; file = files[i++];)
            {
              var filename = path + '/' + file;
              //console.log(path + '/' + file);
              updateStat(filename);
            }
          }

        }
      });
    }

    function stopWatch(path){
      console.log('stopWatch ', path);
      var difInfo = dirMap[path];
      var dirFiles = difInfo.files;

      for (var i = 0, filename; filename = dirFiles[i]; i++)
      {
        var fullPath = path + '/' + filename;

        if (dirMap[fullPath])
          stopWatch(fullPath);

        delete fileMap[fullPath];
      }

      difInfo.watcher.close();

      delete dirMap[path];
    }

    return {
      watch: function(path){
        try {
          var stat = fs.statSync(path);
        } catch(e) {
          console.warn('Error on read path `' + path + '`:' + e);
          return;
        }

        if (!stat.isDirectory())
        {
          console.warn('Error path `' + path + '` is not a folder');
          return;
        }

        if (!dirMap[path])
        {
          lookup(path);
          console.info('Folder `' + path + '` is observing now');
        }
        else
          console.warn('Folder `' + path + '` is already observing');
      },
      unwatch: function(path){
        if (dirMap[path])
        {
          stopWatch(path);
          console.info('Folder `' + path + '` is NOT observing now');
        }
        else
          console.warn('Folder `' + path + '` isn\'t observing yet');
      },
      getFiles: function(path){
        var result = [];

        for (var filename in fileMap)
        {
          if (filename != '/')
          {
            result.push({
              filename: filename,
              type: fileMap[filename].type,
              lastUpdate: fileMap[filename].mtime,
              content: null//fileMap[filename].content
            });
          }
        }

        return result;
      },
      isObserveFile: function(filename){
        return !!fileMap[filename];
      },
      readFile: readFile
    }

  })();

  var createCallback = function(filename, fileType, lastUpdate){
    var fileInfo = {
      filename: normPath(filename),
      type: fileType,
      lastUpdate: lastUpdate
    };

    for (var k in io.sockets.sockets)
    {
      var socket = io.sockets.sockets[k];
      if (socket)
      {
        socket.emit('newFile', fileInfo);
      }
    }
  }
  var updateCallback = function(fileInfo){
    fileInfo.filename = normPath(fileInfo.filename);

    for (var k in io.sockets.sockets)
    {
      var socket = io.sockets.sockets[k];
      if (socket)
      {
        socket.emit('updateFile', fileInfo);
      }
    }
  }

  var deleteCallback = function(filename){
    filename = normPath(filename);

    for (var k in io.sockets.sockets)
    {
      var socket = io.sockets.sockets[k];
      if (socket)
      {
        socket.emit('deleteFile', filename.substr(BASE_PATH.length + 1));
      }
    }
  }

  function arrayAdd(array, item){
    var pos = array.indexOf(item);
    if (pos == -1)
    {
      array.push(item);
      return true;
    }
  }

  function arrayRemove(array, item){
    var pos = array.indexOf(item);
    if (pos != -1)
    {
      array.splice(pos, 1);
      return true;
    }
  }
<commit_msg>server improvements:   - read port from config   - proxy is optional (ignore rewrite rule if no http-proxy)   - use mime module to resolve mime type of filename<commit_after>
  var http = require('http');
  var socket_io = require('socket.io');
  var fs = require('fs');
  var url = require('url');
  var path = require('path');
  var mime = require('mime');

  var fs_debug = false;
  var is_dev = true;

  var BASE_PATH = process.argv[2];
  var port = process.argv[3];
  var configFilename = path.resolve(BASE_PATH, 'server.config');

  //load proxy pathes
  var config = {};
  var rewriteRules = [];
  if (path.existsSync(configFilename))
  {
    console.log('Config found:', configFilename);
    try {
      var data = fs.readFileSync(configFilename);
      config = JSON.parse(String(data));

      if ('port' in config)
      {
        port = Number(config.port);
        console.log('  Set server port:', port);
      }

      if (Array.isArray(config.ignore))
      {
        config.ignore = config.ignore.reduce(function(result, p){
          result[path.resolve(BASE_PATH, p)] = true;
          return result;
        }, {});
        console.log('\n  Ignore pathes:\n    ' + Object.keys(config.ignore).join('\n    '));
      }
      else
        delete config.ignore;

      if (config.rewrite)
      {
        try {
          httpProxy = require('http-proxy');
          console.log('\n  Rewrite rules:');
          for (var key in config.rewrite)
          {
            console.log('    /' + key + '/ -> ' + config.rewrite[key]);
            rewriteRules.push({
              re: new RegExp(key),
              replace: config.rewrite[key]
            });
          }
        } catch(e) {
          console.warn('  Proxy is not supported (requires http-proxy). Rewrite rules ignored.');
        }
      }
      console.log('\nConfig parse done.\n');

    } catch(e) {
      console.warn(e + '\n');
    }
  }

  if (isNaN(port))
    port = 0;

  //proxy

  console.log('Start server');
  //create server
  var proxy;
  var app = http.createServer(function(req, res){

    var location = url.parse(req.url, true, true);
    var host = req.headers.host;

    //proxy request if nececcary
    var pathname = location.pathname.slice(1);
    for (var i = 0, rule; rule = rewriteRules[i]; i++)
    {
      if (rule.re.test(pathname))
      {
        if (!proxy)
        {
          proxy = new httpProxy.HttpProxy({
            target: {
              host: 'localhost',
              port: 80
            }
          });
        }

        //console.log(re);
        proxy.proxyRequest(req, res);
        return;
      }
    }

    var filename = path.normalize(BASE_PATH + location.pathname);

    if (!path.existsSync(filename))
    {
      res.writeHead(404);
      res.end('File ' + filename + ' not found');
    }
    else
    {
      if (fs.statSync(filename).isDirectory())
      {
        if (path.existsSync(filename + '/index.html'))
          filename += '/index.html';
        else
          if (path.existsSync(filename + '/index.htm'))
            filename += '/index.htm';
          else
          {
            res.writeHead(404);
            res.end('Path ' + filename + ' is not file');
          }
      }

      var ext = path.extname(filename);

      fs.readFile(filename, function(err, data){
        if (err)
        {
          res.writeHead(500);
          res.end('Can\'t read file ' + filename + ', error: ' + err);
        }
        else
        {
          res.writeHead(200, {
            'Content-Type': mime.lookup(filename, 'text/plain') //MIME_TYPE[ext.slice(1)] || 'text/plain'
          });

          if (ext == '.html' || ext == '.htm')
          {
            fs.readFile(__dirname + '/client.js', function(err, clientFileData){
              if (!err)
              {
                data = String(data).replace(/<\/body>/, '');
                res.end(data);
              }
            });
          }
          else
          {
            res.end(data);
          }
        }
      });
    }
  });

  app.listen(port, function(){
    var port = app.address().port;
    console.log('Server is online, listen for http://localhost:' + port + '\nWatching changes for path: ' + BASE_PATH);
  });
  var io = socket_io.listen(app);

  //
  // Messaging
  //

  io.sockets.on('connection', function(socket){
    socket.on('saveFile', function(filename, content){
      console.log('save file', arguments);

      var fname = path.normalize(BASE_PATH + '/' + filename);
      var fnKey = normPath(fname);

      if (fsWatcher.isObserveFile(fnKey))
      {
        fs.writeFile(fname, content, function (err) {
          if (err)
          {
            socket.emit('fileSaveError', {
              filename: filename,
              message: err
            });
          }
          else
          {
            socket.emit('fileSaved', fnKey);
          }
        });
      }
      else
        socket.emit('fileSaveError', {
          filename: filename,
          message: 'file not observable'
        });
    });

    socket.on('createFile', function(filename, content){
      console.log('create file', arguments);

      var fname = BASE_PATH + '/' + filename;

      if (!path.existsSync(fname) && path.existsSync(path.dirname(fname)))
      {
        fs.writeFile(fname, '', function (err) {
          if (err)
          {
            socket.emit('error', {
              operation: 'create file',
              message: err
            });
          }
          else
          {
            socket.emit('fileSaved', filename);
          }
        });
      }
      else
        socket.emit('error', {
          operation: 'create file',
          message: 'bad filename'
        });
    });

    socket.on('readFile', function(filename, content){
      console.log('read file', arguments);

      fsWatcher.readFile(BASE_PATH + '/' + filename);
    });

    socket.on('observe', function(rel, fspath){
      fsWatcher.watch(BASE_PATH);
      socket.emit('observeReady', fsWatcher.getFiles(BASE_PATH + '/'));
    });
  });

  io.sockets.on('disconnect', function(socket){
    //socket.files.forEach(fsWatcher.removeContentObserver);
    delete socket.pathes;
  });

  function normPath(filename){
    return '/' + path.relative(BASE_PATH, path.resolve(BASE_PATH, filename)).replace(/\\/g, '/')
  }

  //
  // File System Watcher
  //

  var fsWatcher = (function(){
    var fileMap = {};
    var dirMap = {};

    function readFile(filename){
      if (path.extname(filename) == '.css' || path.extname(filename) == '.tmpl' || path.extname(filename) == '.txt' || path.extname(filename) == '.json')
      {
        fs.readFile(filename, 'utf8', function(err, data){
          console.log('READ FILE:', filename);
          if (!err)
          {
            console.log('key', path.relative(BASE_PATH, filename).replace(/\\/g, '/'));

            var fileInfo = fileMap[normPath(filename)];
            var newContent = String(data).replace(/\r\n?|\n\r?/g, '\n');

            var newFileInfo = {
              filename: filename,
              lastUpdate: fileInfo.mtime
            };

            if (newContent !== fileInfo.content)
            {
              fileInfo.content = newContent;
            }

            newFileInfo.content = newContent;

            updateCallback(newFileInfo);
          }
          else
            console.log('   \033[31merror of file read (' + filename + '): ' + err + ' \033[39m');
        });
      }
    }

    function updateStat(filename){
      fs.stat(filename, function(err, stats){
        var fnKey = normPath(filename);

        if (err)
        {
          console.log('updateStat error:', err);
        }
        else
        {
          var fileInfo = fileMap[fnKey];

          if (!fileMap[fnKey])
          {
            var fileType = stats.isDirectory() ? 'dir' : 'file';

            fileMap[fnKey] = {
              mtime: stats.mtime,
              type: fileType,
              listeners: [],
              content: null
            };

            // event!! new file
            if (filename != BASE_PATH)
            {
              createCallback(filename, fileType, stats.mtime);
              console.log(filename + ' - found');
            }

            if (fileType == 'dir')
            {
              console.log(filename, path.normalize(filename));
              if (!config.ignore || !config.ignore[path.normalize(filename)])
                lookup(filename);
            }
          }
          else
          {
            if (fileMap[fnKey].type == 'file' && stats.mtime - fileMap[fnKey].mtime)
            {
              fileInfo.mtime = stats.mtime;

              readFile(filename);
            }
          }
        }
      });
    }

    function lookup(path){
      fs.readdir(path, function(err, files){
        if (err)
          console.log('lookup error:', path, err);
        else
        {
          var filename;
          var dirInfo = dirMap[path];

          updateStat(path);

          if (dirInfo)
          {
            var dirFiles = dirInfo.files;
            for (var i = 0, file; file = dirFiles[i++];)
            {
              if (files.indexOf(file) == -1)
              {
                var filename = path + '/' + file;
                delete dirFiles[filename];
                delete fileMap[filename];

                // event!!
                deleteCallback(filename);
                if (fs_debug) console.log(filename + ' - missed'); // file lost
              }
            }
          }
          else
          {
            dirInfo = dirMap[path] = {
              path: path,
              watcher: fs.watch(path, function(event, filename){
                lookup(path);
              })
            };
          }

          dirInfo.files = files;

          if (files.length)
          {
            for (var file, i = 0; file = files[i++];)
            {
              var filename = path + '/' + file;
              //console.log(path + '/' + file);
              updateStat(filename);
            }
          }

        }
      });
    }

    function stopWatch(path){
      console.log('stopWatch ', path);
      var difInfo = dirMap[path];
      var dirFiles = difInfo.files;

      for (var i = 0, filename; filename = dirFiles[i]; i++)
      {
        var fullPath = path + '/' + filename;

        if (dirMap[fullPath])
          stopWatch(fullPath);

        delete fileMap[fullPath];
      }

      difInfo.watcher.close();

      delete dirMap[path];
    }

    return {
      watch: function(path){
        try {
          var stat = fs.statSync(path);
        } catch(e) {
          console.warn('Error on read path `' + path + '`:' + e);
          return;
        }

        if (!stat.isDirectory())
        {
          console.warn('Error path `' + path + '` is not a folder');
          return;
        }

        if (!dirMap[path])
        {
          lookup(path);
          console.info('Folder `' + path + '` is observing now');
        }
        else
          console.warn('Folder `' + path + '` is already observing');
      },
      unwatch: function(path){
        if (dirMap[path])
        {
          stopWatch(path);
          console.info('Folder `' + path + '` is NOT observing now');
        }
        else
          console.warn('Folder `' + path + '` isn\'t observing yet');
      },
      getFiles: function(path){
        var result = [];

        for (var filename in fileMap)
        {
          if (filename != '/')
          {
            result.push({
              filename: filename,
              type: fileMap[filename].type,
              lastUpdate: fileMap[filename].mtime,
              content: null//fileMap[filename].content
            });
          }
        }

        return result;
      },
      isObserveFile: function(filename){
        return !!fileMap[filename];
      },
      readFile: readFile
    }

  })();

  var createCallback = function(filename, fileType, lastUpdate){
    var fileInfo = {
      filename: normPath(filename),
      type: fileType,
      lastUpdate: lastUpdate
    };

    for (var k in io.sockets.sockets)
    {
      var socket = io.sockets.sockets[k];
      if (socket)
      {
        socket.emit('newFile', fileInfo);
      }
    }
  }
  var updateCallback = function(fileInfo){
    fileInfo.filename = normPath(fileInfo.filename);

    for (var k in io.sockets.sockets)
    {
      var socket = io.sockets.sockets[k];
      if (socket)
      {
        socket.emit('updateFile', fileInfo);
      }
    }
  }

  var deleteCallback = function(filename){
    filename = normPath(filename);

    for (var k in io.sockets.sockets)
    {
      var socket = io.sockets.sockets[k];
      if (socket)
      {
        socket.emit('deleteFile', filename.substr(BASE_PATH.length + 1));
      }
    }
  }

  function arrayAdd(array, item){
    var pos = array.indexOf(item);
    if (pos == -1)
    {
      array.push(item);
      return true;
    }
  }

  function arrayRemove(array, item){
    var pos = array.indexOf(item);
    if (pos != -1)
    {
      array.splice(pos, 1);
      return true;
    }
  }
<|endoftext|>


<commit_before>function CulturalCtrl($scope, searchModelService, Variable, CodeDescription, ContinuousVariable, DatasetSources, getCategories) {
   var linkModel = function() {
        // Model/state lives in searchModelService
        $scope.traits = [searchModelService.getModel().getCulturalTraits()];
    };

    $scope.$on('searchModelReset', linkModel); // When model is reset, update our model
    linkModel();
    //triggered by the view when a source is changed
    $scope.sourceChanged = function(trait) {
        trait.source_categories = [];
        trait.source_categories = getCategories.query({query: {source: trait.selectedSource.id}});
    };

    // triggered by the view when a category is changed
    $scope.categoryChanged = function(trait) {
        trait.indexVariables = Variable.query({index_categories: trait.selectedCategory.id, source: trait.selectedSource.id});
		trait.codes = [];
        trait.selectedCode = "";
    };

    // triggered by the view when a trait is changed in the picker
    $scope.traitChanged = function(trait) {
        trait.selectedCode = "";
        if (trait.selectedVariable.data_type == 'Continuous') {
            trait.codes = ContinuousVariable.query({query: {bf_id: trait.selectedVariable.id}});
        } else
            trait.codes = CodeDescription.query({variable: trait.selectedVariable.id });

        //make select all the default
        trait.codes.isSelected = true;
        trait.codes.$promise.then(function(result) {
            result.forEach(function(code) {
               code.isSelected = true;
                if (trait.selected.indexOf(code) == -1) {
                    trait.selected.push(code);
                }
            });
            trait.badgeValue = trait.selected.filter(function(code) { return code.isSelected; }).length;
        });
    };

    // used before searching to extract the codes from the search selection
    // Expects an array of CulturalTraitModel objects
	// this only gets the selected codes for the currently selected cultural trait
    $scope.getSelectedTraitCodes = function() {
		traits = $scope.traits;
		var allCodes = Array.prototype.concat.apply([], traits.map( function(trait) {
			return trait.codes;
		}));
		selectedCodes = allCodes.filter( function(c) { return c.isSelected; }).map( function(c) { return c; });
	   return selectedCodes;
    };

    $scope.traitCodeSelectionChanged = function(trait) {
		currentSelection = $scope.getSelectedTraitCodes();
        if (currentSelection.length == trait.codes.length) trait.codes.isSelected = true;
        else trait.codes.isSelected = false;
		currentSelection.forEach(function(code) {
			if (trait.selected.indexOf(code) == -1) {
			//if selected trait code is not already in the array of selected codes, add it to the array
				trait.selected.push(code);
			}
		});
		trait.badgeValue = trait.selected.filter(function(code) { return code.isSelected; }).length;
	};

	$scope.selectAllChanged = function(trait) {
		if (trait.codes.isSelected) {
			trait.codes.forEach(function(code){ code.isSelected = true;
			if (trait.selected.indexOf(code) == -1) {
				trait.selected.push(code);
			}
			});
		} else { trait.codes.forEach(function(code){ code.isSelected = false; });}

		trait.badgeValue = trait.selected.filter(function(code) { return code.isSelected; }).length;

	};

    // wired to the search button. Gets the code ids, adds cultural to the query, and invokes the search
    $scope.doSearch = function() {
        $scope.search();
    };
}
<commit_msg>Cultural search saves user's search state instead of resetting each time variable is selected<commit_after>function CulturalCtrl($scope, searchModelService, Variable, CodeDescription, ContinuousVariable, DatasetSources, getCategories) {
   var linkModel = function() {
        // Model/state lives in searchModelService
        $scope.traits = [searchModelService.getModel().getCulturalTraits()];
        $scope.alreadySelected = []; //keeps track of traits the user has already selected
    };

    $scope.$on('searchModelReset', linkModel); // When model is reset, update our model
    linkModel();
    //triggered by the view when a source is changed
    $scope.sourceChanged = function(trait) {
        trait.source_categories = [];
        trait.source_categories = getCategories.query({query: {source: trait.selectedSource.id}});
    };

    // triggered by the view when a category is changed
    $scope.categoryChanged = function(trait) {
        trait.indexVariables = Variable.query({index_categories: trait.selectedCategory.id, source: trait.selectedSource.id});
		trait.codes = [];
        trait.selectedCode = "";
    };

    // triggered by the view when a trait is changed in the picker
    $scope.traitChanged = function(trait) {
        trait.selectedCode = "";
        if (trait.selectedVariable.data_type == 'Continuous') {
            trait.codes = ContinuousVariable.query({query: {bf_id: trait.selectedVariable.id}});
        } else
            trait.codes = CodeDescription.query({variable: trait.selectedVariable.id });
        trait.selected = trait.selected.filter(function(code) { return code.isSelected; });

        //make select all the default
        if ($scope.alreadySelected.indexOf(trait.selectedVariable.id) == -1) {
            trait.codes.isSelected = true;
            trait.codes.$promise.then(function(result) {
                result.forEach(function(code) {
                   code.isSelected = true;
                   if (trait.selected.map(function(code) { return code.id; }).indexOf(code.id) == -1) {
                        trait.selected.push(code);
                   }
                });
                trait.badgeValue = trait.selected.filter(function(code) { return code.isSelected; }).length;
            });
            $scope.alreadySelected.push(trait.selectedVariable.id);
        } else {
            trait.codes.$promise.then(function(result) {
                result.forEach(function(code) {
                    if (trait.selected.map(function(code) { return code.id; }).indexOf(code.id) != -1) {
                        code.isSelected = true;
                   }
                });
                trait.badgeValue = trait.selected.length;
            });
        }
    };

    // used before searching to extract the codes from the search selection
    // Expects an array of CulturalTraitModel objects
	// this only gets the selected codes for the currently selected cultural trait
    $scope.getSelectedTraitCodes = function() {
		traits = $scope.traits;
		var allCodes = Array.prototype.concat.apply([], traits.map( function(trait) {
			return trait.codes;
		}));
		return allCodes.filter( function(c) { return c.isSelected; });
    };

    function removeCode(trait, code) {
        var index = -1;
        for (var i = 0; i < trait.selected.length; i++) {
            if (trait.selected[i].id == code.id) {
                index = i;
                break;
            }
        }
        if (index > -1) {
            trait.selected.splice(index, 1);
        }
    };

    $scope.traitCodeSelectionChanged = function(trait) {
		currentSelection = $scope.getSelectedTraitCodes();
        if (currentSelection.length == trait.codes.length) trait.codes.isSelected = true;
        else trait.codes.isSelected = false;
        trait.selected = trait.selected.filter(function(code) { return code.isSelected; });
        trait.codes.forEach(function(code) {
            if (code.isSelected) {
                if (trait.selected.map(function(code) { return code.id; }).indexOf(code.id) == -1) {
                    trait.selected.push(code);
                }
            } else {
                removeCode(trait, code);
            }
        });
		trait.badgeValue = trait.selected.length;
	};

	$scope.selectAllChanged = function(trait) {
        trait.selected = trait.selected.filter(function(code) { return code.isSelected; });
		if (trait.codes.isSelected) {
			trait.codes.forEach(function(code){
                code.isSelected = true;
                if (trait.selected.map(function(code) { return code.id; }).indexOf(code.id) == -1) trait.selected.push(code);
			});
		} else {
            trait.codes.forEach(function(code){
                code.isSelected = false;
                removeCode(trait, code);
            });
        }
		trait.badgeValue = trait.selected.length;
	};

    // wired to the search button. Gets the code ids, adds cultural to the query, and invokes the search
    $scope.doSearch = function() {
        $scope.search();
    };
}
<|endoftext|>