<commit_before>/**
 * Copyright 2016 Google Inc.
 *
 * 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.
**/
'use strict';

require('geckodriver');
require('chromedriver');

const seleniumAssistant = require('selenium-assistant');
const fs = require('fs');
const del = require('del');
const mkdirp = require('mkdirp');
const seleniumFirefox = require('selenium-webdriver/firefox');

const logHelper = require('./helper/log-helper.js');
const APIServer = require('./server/api-server.js');
const TestSuite = require('./model/test-suite.js');

// This may be needed: https://github.com/angular/protractor/issues/2419#issuecomment-213112857

class WPTS {
  constructor(port) {
    port = port ? port : 8090;

    this._availableTestSuiteId = 0;
    this._testSuites = {};
    this._supportedBrowsers = [
      'chrome',
      'firefox',
    ];
    this._supportedBrowserVersions = [
      'stable',
      'beta',
      'unstable',
    ];

    this._apiServer = new APIServer(port);
    this._apiServer.on('start-test-suite', this.startTestSuite.bind(this));
    this._apiServer.on('end-test-suite', this.endTestSuite.bind(this));
    this._apiServer.on('get-subscription', this.getSubscription.bind(this));
    this._apiServer.on('get-notification-status',
      this.getNotificationStatus.bind(this));

    logHelper.setLogFile('./module.log');
  }

  setLogFile(logFile) {
    logHelper.setLogFile(logFile);
  }

  downloadBrowsers() {
    return Promise.all([
      seleniumAssistant.downloadLocalBrowser('firefox', 'stable', 48),
      seleniumAssistant.downloadLocalBrowser('firefox', 'beta', 48),
      seleniumAssistant.downloadLocalBrowser('firefox', 'unstable', 48),
      seleniumAssistant.downloadLocalBrowser('chrome', 'stable', 48),
      seleniumAssistant.downloadLocalBrowser('chrome', 'beta', 48),
      seleniumAssistant.downloadLocalBrowser('chrome', 'unstable', 48),
    ]);
  }

  startService() {
    return this.downloadBrowsers()
    .then(() => {
      return this._apiServer.startListening();
    });
  }

  endService() {
    const testSuiteIds = Object.keys(this._testSuites);
    const promises = testSuiteIds.map((testSuiteId) => {
      const testSuite = this._testSuites[testSuiteId];
      return testSuite.end()
      .then(() => {
        delete this._testSuites[testSuiteId];
      });
    });

    return this._apiServer.kill()
    .then(() => {
      return Promise.all(promises);
    })
    .then(() => {
      this._availableTestSuiteId = 0;
    });
  }

  startTestSuite(res) {
    logHelper.info('Received start-test-suite call.');
    const newTestSuite = new TestSuite(this._availableTestSuiteId);
    this._availableTestSuiteId++;
    this._testSuites[newTestSuite.id] = newTestSuite;
    logHelper.info('    Created new test suite. ' + this._availableTestSuiteId);
    APIServer.sendValidResponse(res, {testSuiteId: newTestSuite.id});
    logHelper.info('    Response was sent');
  }

  endTestSuite(res, args) {
    const requiredFields = ['testSuiteId'];
    if (this.missingRequiredArgs(res, args, requiredFields)) {
      return;
    }

    if (!this.validTestSuiteId(res, args.testSuiteId)) {
      return;
    }

    return this._testSuites[args.testSuiteId].end()
    .then(() => {
      delete this._testSuites[args.testSuiteId];
      APIServer.sendValidResponse(res);
    })
    .catch((err) => {
      APIServer.sendErrorResponse(res, 'webdriver_issue', 'An issue ' +
        'occured while attempting to get the subscription: ' + err.message);
    });
  }

  getSubscription(res, args) {
    const requiredFields = ['testSuiteId', 'browserName', 'browserVersion'];
    if (this.missingRequiredArgs(res, args, requiredFields)) {
      return;
    }

    if (!this.validTestSuiteId(res, args.testSuiteId)) {
      return;
    }

    if (!args.browserName ||
      this._supportedBrowsers.indexOf(args.browserName) === -1) {
      APIServer.sendErrorResponse(res, 'invalid_browser_name', `browserName ` +
        `should be one of the following values: ` +
        `${this._supportedBrowsers.join(', ')}. ` +
        `Found ${(typeof args.browserName)}.`);
      return;
    }

    if (!args.browserVersion ||
      this._supportedBrowserVersions.indexOf(args.browserVersion) === -1) {
      APIServer.sendErrorResponse(res, 'invalid_browser_version',
        `browserVersion should be one of the following values: ` +
        `${this._supportedBrowserVersions.join(', ')}. ` +
        `Found ${(typeof args.browserVersion)}.`);
      return;
    }

    const webDriverInstance =
      seleniumAssistant.getLocalBrowser(args.browserName, args.browserVersion);
    if (!webDriverInstance) {
      APIServer.sendErrorResponse(res, 'browser_not_found',
        `Unable to find the requested browser. This is likely an issue` +
        `the push testing service.`);
      return;
    }

    if (webDriverInstance.getId() === 'firefox' &&
      webDriverInstance.getVersionNumber() <= 48) {
      APIServer.sendErrorResponse(res, 'bad_browser_support',
        `Unforuntately Firefox version 49 and below has poor selenium ` +
        `support or doesn't allow notifications automatically and isn't ` +
        `supported as a result.`);
      return;
    }

    const optionalArgs = {};
    if (args.vapidPublicKey) {
      if (typeof args.vapidPublicKey !== 'string') {
        APIServer.sendErrorResponse(res, 'invalid_vapid_key',
          `Your vapid public key must be a string.`);
        return;
      }

      optionalArgs.vapidPublicKey = args.vapidPublicKey;
    }

    if (args.gcmSenderId) {
      if (typeof args.gcmSenderId !== 'string') {
        APIServer.sendErrorResponse(res, 'invalid_gcm_sender_id',
          `Your GCM Sender ID must be a string.`);
        return;
      }

      optionalArgs.gcmSenderId = args.gcmSenderId;
    }

    return this.initiateTestInstance(args.testSuiteId, optionalArgs,
      webDriverInstance)
    .then((testDetails) => {
      APIServer.sendValidResponse(res, testDetails);
    })
    .catch((err) => {
      if (err.code && err.message) {
        APIServer.sendErrorResponse(res, err.code, 'An issue ' +
          'occured while attempting to get the subscription: ' + err.message);
        return;
      }

      APIServer.sendErrorResponse(res, 'webdriver_issue', 'An issue ' +
        'occured while attempting to get the subscription: ' + err.message);
    });
  }

  getNotificationStatus(res, args) {
    const requiredFields = ['testSuiteId', 'testId'];
    if (this.missingRequiredArgs(res, args, requiredFields)) {
      return;
    }

    if (!this.validTestId(res, args.testSuiteId, args.testId)) {
      return;
    }
    // Get the available test
    const testInstance = this._testSuites[args.testSuiteId]
      .getTestInstance(args.testId);

    // get the available notification status
    return testInstance.wait(() => {
      return testInstance.executeScript(() => {
        return window.PUSH_TESTING_SERVICE.receivedMessages.length > 0;
      })
      .then((isValid) => {
        if (isValid) {
          return true;
        }

        return new Promise((resolve) => {
          setTimeout(() => resolve(false), 500);
        });
      });
    }, 60000)
    .then(() => {
      return testInstance.executeScript(() => {
        const messages = window.PUSH_TESTING_SERVICE.receivedMessages;
        window.PUSH_TESTING_SERVICE.receivedMessages = [];
        return messages;
      });
    })
    .then((receivedMessages) => {
      APIServer.sendValidResponse(res, {messages: receivedMessages});
    })
    .catch((err) => {
      APIServer.sendErrorResponse(res, 'web_driver_error', 'An error ' +
        'occured while attempting to check the notification status. ' +
        err.message);
    });
  }

  initiateTestInstance(testSuiteId, optionalArgs, seleniumAssistantBrowser) {
    const tempPreferenceFile = './temp/blink';
    return del('./temp')
    .then(() => {
      if (seleniumAssistantBrowser.getId() === 'chrome' ||
        seleniumAssistantBrowser.getId() === 'opera') {
        /* eslint-disable camelcase */
        const blinkPreferences = {
          profile: {
            content_settings: {
              exceptions: {
                notifications: {},
              },
            },
          },
        };
        blinkPreferences.profile.content_settings.exceptions
          .notifications[this._apiServer.getUrl() + ',*'] = {
            setting: 1,
          };

        // Write to file
        mkdirp.sync(`${tempPreferenceFile}/Default`);

        fs.writeFileSync(`${tempPreferenceFile}/Default/Preferences`,
          JSON.stringify(blinkPreferences));

        const options = seleniumAssistantBrowser.getSeleniumOptions();
        options.addArguments(`user-data-dir=${tempPreferenceFile}/`);
      } else if (seleniumAssistantBrowser.getId() ===
        'firefox') {
        const ffProfile = new seleniumFirefox.Profile();
        ffProfile.setPreference('security.turn_off_all_security_so_that_' +
          'viruses_can_take_over_this_computer', true);
        ffProfile.setPreference('dom.push.testing.ignorePermission', true);
        ffProfile.setPreference('notification.prompt.testing', true);
        ffProfile.setPreference('notification.prompt.testing.allow', true);
        seleniumAssistantBrowser.getSeleniumOptions().setProfile(ffProfile);
      }

      return seleniumAssistantBrowser.getSeleniumDriver();
    })
    .then((driver) => {
      const testSuite = this._testSuites[testSuiteId];
      const testInstanceId = testSuite.addTestInstance(driver);

      let urlGETArgs = '';
      const optionalArgKeys = Object.keys(optionalArgs);
      if (optionalArgKeys.length > 0) {
        const urlArgs = optionalArgKeys.map((argKey) => {
          return `${argKey}=${optionalArgs[argKey]}`;
        });
        urlGETArgs = '?' + urlArgs.join('&');
      }

      return driver.get(this._apiServer.getUrl() + '/' + urlGETArgs)
      .then(() => {
        // This adds extra code to make notifications auto-grant perission
        if (seleniumAssistantBrowser.getId() === 'firefox') {
          driver.setContext(seleniumFirefox.Context.CHROME);
          return driver.executeScript((url) => {
            /* global Components, Services */
            Components.utils.import('resource://gre/modules/Services.jsm');
            const uri = Services.io.newURI(url, null, null);
            const principal = Services.scriptSecurityManager
              .getNoAppCodebasePrincipal(uri);
            Services.perms.addFromPrincipal(
              principal, 'desktop-notification', Services.perms.ALLOW_ACTION);
          }, this._apiServer.getUrl())
          .then(() => {
            driver.setContext(seleniumFirefox.Context.CONTENT);
          });
        }
      })
      .then(() => {
        return driver.wait(() => {
          return driver.executeScript(() => {
            /* eslint-env browser */
            return window.PUSH_TESTING_SERVICE &&
              window.PUSH_TESTING_SERVICE.loaded;
          });
        });
      })
      .then(() => {
        return driver.executeScript(() => {
          window.PUSH_TESTING_SERVICE.start();
        });
      })
      .then(() => {
        return driver.wait(() => {
          return driver.executeScript(() => {
            return (typeof window.PUSH_TESTING_SERVICE.swRegistered) !==
              'undefined';
          });
        });
      })
      .then(() => {
        return driver.executeScript(() => {
          return window.PUSH_TESTING_SERVICE.swRegistered;
        });
      })
      .then((swRegistered) => {
        if (swRegistered.error) {
          const errorDetails = {
            code: 'unable_to_reg_service_worker',
            message: `There was an error when registering the service worker.` +
            ` "${swRegistered.error}".`,
          };
          throw errorDetails;
        }
      })
      .then(() => {
        return driver.wait(() => {
          return driver.executeScript(() => {
            /* eslint-env browser */
            return (typeof window.PUSH_TESTING_SERVICE.subscription) !==
              'undefined';
          });
        });
      })
      .then(() => {
        return driver.executeScript(() => {
          /* eslint-env browser */
          return window.PUSH_TESTING_SERVICE.subscription;
        });
      })
      .then((subscription) => {
        if (subscription.error) {
          const errorDetails = {
            code: 'unable_to_get_subscription',
            message: subscription.error,
          };
          throw errorDetails;
        }

        return {
          testId: testInstanceId,
          subscription: subscription,
        };
      });
    });
  }

  missingRequiredArgs(res, args, requiredFields) {
    // Clone the required fields array
    const requiredFieldsClone = requiredFields.slice(0);
    Object.keys(args).forEach((key) => {
      const fieldIndex = requiredFieldsClone.indexOf(key);
      if (fieldIndex >= 0) {
        if (typeof args[key] !== 'undefined' && args[key] !== null) {
          requiredFieldsClone.splice(fieldIndex, 1);
        }
      }
    });

    if (requiredFieldsClone.length > 0) {
      APIServer.sendErrorResponse(res, 'missing_required_args', `Required ` +
        `arguments are missing. Required fields: ` +
        `${requiredFields.join(', ')}. Received: ` +
        `${Object.keys(args).join(', ')}`);
    }

    return requiredFieldsClone.length > 0;
  }

  validTestSuiteId(res, testSuiteId) {
    if (typeof testSuiteId !== 'number') {
      APIServer.sendErrorResponse(res, 'invalid_variable_type', `testSuiteId ` +
        `should be a number, found ${(typeof testSuiteId)}.`);
      return false;
    }

    if (!this._testSuites[testSuiteId]) {
      APIServer.sendErrorResponse(res, 'invalid_test_suite_id', `testSuiteId ` +
        `passed in doesn't exist: ${testSuiteId}.`);
      return false;
    }

    return true;
  }

  validTestId(res, testSuiteId, testId) {
    if (!this.validTestSuiteId(res, testSuiteId)) {
      return false;
    }

    if (typeof testId !== 'number') {
      APIServer.sendErrorResponse(res, 'invalid_variable_type', `testId ` +
        `should be a number, found ${(typeof testId)}.`);
      return false;
    }

    const testSuite = this._testSuites[testSuiteId];
    const testInstance = testSuite.getTestInstance(testId);
    if (!testInstance) {
      APIServer.sendErrorResponse(res, 'invalid_test_id', `testId ` +
        `was not found as a valid test, received testId: ${testId}.`);
      return false;
    }
    return true;
  }
}

module.exports = WPTS;
<commit_msg>Handling a change to the Firefox security API (#10)<commit_after>/**
 * Copyright 2016 Google Inc.
 *
 * 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.
**/
'use strict';

require('geckodriver');
require('chromedriver');

const seleniumAssistant = require('selenium-assistant');
const fs = require('fs');
const del = require('del');
const mkdirp = require('mkdirp');
const seleniumFirefox = require('selenium-webdriver/firefox');

const logHelper = require('./helper/log-helper.js');
const APIServer = require('./server/api-server.js');
const TestSuite = require('./model/test-suite.js');

// This may be needed: https://github.com/angular/protractor/issues/2419#issuecomment-213112857

class WPTS {
  constructor(port) {
    port = port ? port : 8090;

    this._availableTestSuiteId = 0;
    this._testSuites = {};
    this._supportedBrowsers = [
      'chrome',
      'firefox',
    ];
    this._supportedBrowserVersions = [
      'stable',
      'beta',
      'unstable',
    ];

    this._apiServer = new APIServer(port);
    this._apiServer.on('start-test-suite', this.startTestSuite.bind(this));
    this._apiServer.on('end-test-suite', this.endTestSuite.bind(this));
    this._apiServer.on('get-subscription', this.getSubscription.bind(this));
    this._apiServer.on('get-notification-status',
      this.getNotificationStatus.bind(this));

    logHelper.setLogFile('./module.log');
  }

  setLogFile(logFile) {
    logHelper.setLogFile(logFile);
  }

  downloadBrowsers() {
    return Promise.all([
      seleniumAssistant.downloadLocalBrowser('firefox', 'stable', 48),
      seleniumAssistant.downloadLocalBrowser('firefox', 'beta', 48),
      seleniumAssistant.downloadLocalBrowser('firefox', 'unstable', 48),
      seleniumAssistant.downloadLocalBrowser('chrome', 'stable', 48),
      seleniumAssistant.downloadLocalBrowser('chrome', 'beta', 48),
      seleniumAssistant.downloadLocalBrowser('chrome', 'unstable', 48),
    ]);
  }

  startService() {
    return this.downloadBrowsers()
    .then(() => {
      return this._apiServer.startListening();
    });
  }

  endService() {
    const testSuiteIds = Object.keys(this._testSuites);
    const promises = testSuiteIds.map((testSuiteId) => {
      const testSuite = this._testSuites[testSuiteId];
      return testSuite.end()
      .then(() => {
        delete this._testSuites[testSuiteId];
      });
    });

    return this._apiServer.kill()
    .then(() => {
      return Promise.all(promises);
    })
    .then(() => {
      this._availableTestSuiteId = 0;
    });
  }

  startTestSuite(res) {
    logHelper.info('Received start-test-suite call.');
    const newTestSuite = new TestSuite(this._availableTestSuiteId);
    this._availableTestSuiteId++;
    this._testSuites[newTestSuite.id] = newTestSuite;
    logHelper.info('    Created new test suite. ' + this._availableTestSuiteId);
    APIServer.sendValidResponse(res, {testSuiteId: newTestSuite.id});
    logHelper.info('    Response was sent');
  }

  endTestSuite(res, args) {
    const requiredFields = ['testSuiteId'];
    if (this.missingRequiredArgs(res, args, requiredFields)) {
      return;
    }

    if (!this.validTestSuiteId(res, args.testSuiteId)) {
      return;
    }

    return this._testSuites[args.testSuiteId].end()
    .then(() => {
      delete this._testSuites[args.testSuiteId];
      APIServer.sendValidResponse(res);
    })
    .catch((err) => {
      APIServer.sendErrorResponse(res, 'webdriver_issue', 'An issue ' +
        'occured while attempting to get the subscription: ' + err.message);
    });
  }

  getSubscription(res, args) {
    const requiredFields = ['testSuiteId', 'browserName', 'browserVersion'];
    if (this.missingRequiredArgs(res, args, requiredFields)) {
      return;
    }

    if (!this.validTestSuiteId(res, args.testSuiteId)) {
      return;
    }

    if (!args.browserName ||
      this._supportedBrowsers.indexOf(args.browserName) === -1) {
      APIServer.sendErrorResponse(res, 'invalid_browser_name', `browserName ` +
        `should be one of the following values: ` +
        `${this._supportedBrowsers.join(', ')}. ` +
        `Found ${(typeof args.browserName)}.`);
      return;
    }

    if (!args.browserVersion ||
      this._supportedBrowserVersions.indexOf(args.browserVersion) === -1) {
      APIServer.sendErrorResponse(res, 'invalid_browser_version',
        `browserVersion should be one of the following values: ` +
        `${this._supportedBrowserVersions.join(', ')}. ` +
        `Found ${(typeof args.browserVersion)}.`);
      return;
    }

    const webDriverInstance =
      seleniumAssistant.getLocalBrowser(args.browserName, args.browserVersion);
    if (!webDriverInstance) {
      APIServer.sendErrorResponse(res, 'browser_not_found',
        `Unable to find the requested browser. This is likely an issue` +
        `the push testing service.`);
      return;
    }

    if (webDriverInstance.getId() === 'firefox' &&
      webDriverInstance.getVersionNumber() <= 48) {
      APIServer.sendErrorResponse(res, 'bad_browser_support',
        `Unforuntately Firefox version 49 and below has poor selenium ` +
        `support or doesn't allow notifications automatically and isn't ` +
        `supported as a result.`);
      return;
    }

    const optionalArgs = {};
    if (args.vapidPublicKey) {
      if (typeof args.vapidPublicKey !== 'string') {
        APIServer.sendErrorResponse(res, 'invalid_vapid_key',
          `Your vapid public key must be a string.`);
        return;
      }

      optionalArgs.vapidPublicKey = args.vapidPublicKey;
    }

    if (args.gcmSenderId) {
      if (typeof args.gcmSenderId !== 'string') {
        APIServer.sendErrorResponse(res, 'invalid_gcm_sender_id',
          `Your GCM Sender ID must be a string.`);
        return;
      }

      optionalArgs.gcmSenderId = args.gcmSenderId;
    }

    return this.initiateTestInstance(args.testSuiteId, optionalArgs,
      webDriverInstance)
    .then((testDetails) => {
      APIServer.sendValidResponse(res, testDetails);
    })
    .catch((err) => {
      if (err.code && err.message) {
        APIServer.sendErrorResponse(res, err.code, 'An issue ' +
          'occured while attempting to get the subscription: ' + err.message);
        return;
      }

      APIServer.sendErrorResponse(res, 'webdriver_issue', 'An issue ' +
        'occured while attempting to get the subscription: ' + err.message);
    });
  }

  getNotificationStatus(res, args) {
    const requiredFields = ['testSuiteId', 'testId'];
    if (this.missingRequiredArgs(res, args, requiredFields)) {
      return;
    }

    if (!this.validTestId(res, args.testSuiteId, args.testId)) {
      return;
    }
    // Get the available test
    const testInstance = this._testSuites[args.testSuiteId]
      .getTestInstance(args.testId);

    // get the available notification status
    return testInstance.wait(() => {
      return testInstance.executeScript(() => {
        return window.PUSH_TESTING_SERVICE.receivedMessages.length > 0;
      })
      .then((isValid) => {
        if (isValid) {
          return true;
        }

        return new Promise((resolve) => {
          setTimeout(() => resolve(false), 500);
        });
      });
    }, 60000)
    .then(() => {
      return testInstance.executeScript(() => {
        const messages = window.PUSH_TESTING_SERVICE.receivedMessages;
        window.PUSH_TESTING_SERVICE.receivedMessages = [];
        return messages;
      });
    })
    .then((receivedMessages) => {
      APIServer.sendValidResponse(res, {messages: receivedMessages});
    })
    .catch((err) => {
      APIServer.sendErrorResponse(res, 'web_driver_error', 'An error ' +
        'occured while attempting to check the notification status. ' +
        err.message);
    });
  }

  initiateTestInstance(testSuiteId, optionalArgs, seleniumAssistantBrowser) {
    const tempPreferenceFile = './temp/blink';
    return del('./temp')
    .then(() => {
      if (seleniumAssistantBrowser.getId() === 'chrome' ||
        seleniumAssistantBrowser.getId() === 'opera') {
        /* eslint-disable camelcase */
        const blinkPreferences = {
          profile: {
            content_settings: {
              exceptions: {
                notifications: {},
              },
            },
          },
        };
        blinkPreferences.profile.content_settings.exceptions
          .notifications[this._apiServer.getUrl() + ',*'] = {
            setting: 1,
          };

        // Write to file
        mkdirp.sync(`${tempPreferenceFile}/Default`);

        fs.writeFileSync(`${tempPreferenceFile}/Default/Preferences`,
          JSON.stringify(blinkPreferences));

        const options = seleniumAssistantBrowser.getSeleniumOptions();
        options.addArguments(`user-data-dir=${tempPreferenceFile}/`);
      } else if (seleniumAssistantBrowser.getId() ===
        'firefox') {
        const ffProfile = new seleniumFirefox.Profile();
        ffProfile.setPreference('security.turn_off_all_security_so_that_' +
          'viruses_can_take_over_this_computer', true);
        ffProfile.setPreference('dom.push.testing.ignorePermission', true);
        ffProfile.setPreference('notification.prompt.testing', true);
        ffProfile.setPreference('notification.prompt.testing.allow', true);
        seleniumAssistantBrowser.getSeleniumOptions().setProfile(ffProfile);
      }

      return seleniumAssistantBrowser.getSeleniumDriver();
    })
    .then((driver) => {
      const testSuite = this._testSuites[testSuiteId];
      const testInstanceId = testSuite.addTestInstance(driver);

      let urlGETArgs = '';
      const optionalArgKeys = Object.keys(optionalArgs);
      if (optionalArgKeys.length > 0) {
        const urlArgs = optionalArgKeys.map((argKey) => {
          return `${argKey}=${optionalArgs[argKey]}`;
        });
        urlGETArgs = '?' + urlArgs.join('&');
      }

      return driver.get(this._apiServer.getUrl() + '/' + urlGETArgs)
      .then(() => {
        // This adds extra code to make notifications auto-grant perission
        if (seleniumAssistantBrowser.getId() === 'firefox') {
          driver.setContext(seleniumFirefox.Context.CHROME);
          return driver.executeScript((url) => {
            /* global Components, Services */
            Components.utils.import('resource://gre/modules/Services.jsm');
            const uri = Services.io.newURI(url, null, null);
            const principal = Services.scriptSecurityManager
              .getCodebasePrincipal(uri);
            Services.perms.addFromPrincipal(
              principal, 'desktop-notification', Services.perms.ALLOW_ACTION);
          }, this._apiServer.getUrl())
          .then(() => {
            driver.setContext(seleniumFirefox.Context.CONTENT);
          });
        }
      })
      .then(() => {
        return driver.wait(() => {
          return driver.executeScript(() => {
            /* eslint-env browser */
            return window.PUSH_TESTING_SERVICE &&
              window.PUSH_TESTING_SERVICE.loaded;
          });
        });
      })
      .then(() => {
        return driver.executeScript(() => {
          window.PUSH_TESTING_SERVICE.start();
        });
      })
      .then(() => {
        return driver.wait(() => {
          return driver.executeScript(() => {
            return (typeof window.PUSH_TESTING_SERVICE.swRegistered) !==
              'undefined';
          });
        });
      })
      .then(() => {
        return driver.executeScript(() => {
          return window.PUSH_TESTING_SERVICE.swRegistered;
        });
      })
      .then((swRegistered) => {
        if (swRegistered.error) {
          const errorDetails = {
            code: 'unable_to_reg_service_worker',
            message: `There was an error when registering the service worker.` +
            ` "${swRegistered.error}".`,
          };
          throw errorDetails;
        }
      })
      .then(() => {
        return driver.wait(() => {
          return driver.executeScript(() => {
            /* eslint-env browser */
            return (typeof window.PUSH_TESTING_SERVICE.subscription) !==
              'undefined';
          });
        });
      })
      .then(() => {
        return driver.executeScript(() => {
          /* eslint-env browser */
          return window.PUSH_TESTING_SERVICE.subscription;
        });
      })
      .then((subscription) => {
        if (subscription.error) {
          const errorDetails = {
            code: 'unable_to_get_subscription',
            message: subscription.error,
          };
          throw errorDetails;
        }

        return {
          testId: testInstanceId,
          subscription: subscription,
        };
      });
    });
  }

  missingRequiredArgs(res, args, requiredFields) {
    // Clone the required fields array
    const requiredFieldsClone = requiredFields.slice(0);
    Object.keys(args).forEach((key) => {
      const fieldIndex = requiredFieldsClone.indexOf(key);
      if (fieldIndex >= 0) {
        if (typeof args[key] !== 'undefined' && args[key] !== null) {
          requiredFieldsClone.splice(fieldIndex, 1);
        }
      }
    });

    if (requiredFieldsClone.length > 0) {
      APIServer.sendErrorResponse(res, 'missing_required_args', `Required ` +
        `arguments are missing. Required fields: ` +
        `${requiredFields.join(', ')}. Received: ` +
        `${Object.keys(args).join(', ')}`);
    }

    return requiredFieldsClone.length > 0;
  }

  validTestSuiteId(res, testSuiteId) {
    if (typeof testSuiteId !== 'number') {
      APIServer.sendErrorResponse(res, 'invalid_variable_type', `testSuiteId ` +
        `should be a number, found ${(typeof testSuiteId)}.`);
      return false;
    }

    if (!this._testSuites[testSuiteId]) {
      APIServer.sendErrorResponse(res, 'invalid_test_suite_id', `testSuiteId ` +
        `passed in doesn't exist: ${testSuiteId}.`);
      return false;
    }

    return true;
  }

  validTestId(res, testSuiteId, testId) {
    if (!this.validTestSuiteId(res, testSuiteId)) {
      return false;
    }

    if (typeof testId !== 'number') {
      APIServer.sendErrorResponse(res, 'invalid_variable_type', `testId ` +
        `should be a number, found ${(typeof testId)}.`);
      return false;
    }

    const testSuite = this._testSuites[testSuiteId];
    const testInstance = testSuite.getTestInstance(testId);
    if (!testInstance) {
      APIServer.sendErrorResponse(res, 'invalid_test_id', `testId ` +
        `was not found as a valid test, received testId: ${testId}.`);
      return false;
    }
    return true;
  }
}

module.exports = WPTS;
<|endoftext|>


<commit_before>import csso from 'csso';
import async from 'async';
import RawSource from 'webpack-sources/lib/RawSource';
import SourceMapSource from 'webpack-sources/lib/SourceMapSource';
import { SourceMapConsumer } from 'source-map';

const filterDefault = file => file.endsWith('.css');
const createRegexpFilter = regex => str => regex.test(str);
const isFilterType = inst => typeof inst === 'function' || inst instanceof RegExp;

export default class CssoWebpackPlugin {
    constructor(options, filter) {
        this.options = options;
        this.filter = filter;

        if (isFilterType(this.options) && typeof this.filter === 'undefined') {
            this.filter = options;
            this.options = undefined;
        }

        if (typeof this.filter === 'undefined') {
            this.filter = filterDefault;
        }

        if (typeof this.filter !== 'function') {
            this.filter = createRegexpFilter(filter);
        }

        this.options = this.options || {};
    }

    apply(compiler) {
        compiler.plugin('compilation', compilation => {
            const options = this.options;
            const optSourceMap = options.sourceMap;

            if (optSourceMap) {
                compilation.plugin('build-module', module => {
                    module.useSourceMap = true;
                });
            }

            compilation.plugin('optimize-assets', (assets, callback) => {
                async.forEach(Object.keys(assets), file => {
                    try {
                        if (!this.filter(file)) {
                            return callback();
                        }

                        let source;
                        let sourceMap;

                        const asset = assets[file];

                        if (asset.sourceAndMap) {
                            const sourceAndMap = asset.sourceAndMap();
                            sourceMap = sourceAndMap.map;
                            source = sourceAndMap.source;
                        } else {
                            sourceMap = asset.map();
                            source = asset.source();
                        }

                        if (Buffer.isBuffer(source)) {
                            source = source.toString('utf-8');
                        }

                        let { css, map } = csso.minify(source, { // eslint-disable-line prefer-const
                            ...options,
                            filename: file,
                            sourceMap: typeof optSourceMap !== 'undefined' ? optSourceMap : Boolean(sourceMap),
                        });

                        if (map && sourceMap) {
                            map.applySourceMap(new SourceMapConsumer(sourceMap), file);
                        }

                        if (!map) {
                            map = sourceMap;
                        }

                        if (map) {
                            compilation.assets[file] = new SourceMapSource(css, file, map, source, sourceMap);
                        } else {
                            compilation.assets[file] = new RawSource(css);
                        }
                    } catch (err) {
                        let error = err;
                        const prefix = `${file} from CssoWebpackPlugin\n`;
                        const { message, parseError, stack } = err;

                        if (parseError) {
                            error = `${message} [${file}:${parseError.line}:${parseError.column}]`;
                        } else {
                            error = `${message} ${stack}`;
                        }

                        compilation.errors.push(new Error(`${prefix}${error}`));
                    }

                    return callback();
                });
            });
        });
    }
}
<commit_msg>Append source map url to bottom bundle file<commit_after>import csso from 'csso';
import async from 'async';
import RawSource from 'webpack-sources/lib/RawSource';
import ConcatSource from 'webpack-sources/lib/ConcatSource';
import SourceMapSource from 'webpack-sources/lib/SourceMapSource';
import { SourceMapConsumer } from 'source-map';

const filterDefault = file => file.endsWith('.css');
const createRegexpFilter = regex => str => regex.test(str);
const isFilterType = inst => typeof inst === 'function' || inst instanceof RegExp;

const SOURCEMAP_INLINE_DEVTOOL = [];
const isInline = devtool => SOURCEMAP_INLINE_DEVTOOL.indexOf(devtool) !== -1;

const sourceMapURL = content => `\n/*# sourceMappingURL=${content} */`;

export default class CssoWebpackPlugin {
    constructor(options, filter) {
        this.options = options;
        this.filter = filter;

        if (isFilterType(this.options) && typeof this.filter === 'undefined') {
            this.filter = options;
            this.options = undefined;
        }

        if (typeof this.filter === 'undefined') {
            this.filter = filterDefault;
        }

        if (typeof this.filter !== 'function') {
            this.filter = createRegexpFilter(filter);
        }

        this.options = this.options || {};
    }

    apply(compiler) {
        compiler.plugin('compilation', compilation => {
            const options = this.options;
            const optSourceMap = options.sourceMap;
            const devtool = compiler.options.devtool;

            if (optSourceMap) {
                compilation.plugin('build-module', module => {
                    module.useSourceMap = true;
                });
            }

            compilation.plugin('optimize-assets', (assets, callback) => {
                async.forEach(Object.keys(assets), file => {
                    try {
                        if (!this.filter(file)) {
                            return callback();
                        }

                        let source;
                        let sourceMap;

                        const asset = assets[file];

                        if (asset.sourceAndMap) {
                            const sourceAndMap = asset.sourceAndMap();
                            sourceMap = sourceAndMap.map;
                            source = sourceAndMap.source;
                        } else {
                            sourceMap = asset.map();
                            source = asset.source();
                        }

                        if (Buffer.isBuffer(source)) {
                            source = source.toString('utf-8');
                        }

                        let { css, map } = csso.minify(source, { // eslint-disable-line prefer-const
                            ...options,
                            filename: file,
                            sourceMap: typeof optSourceMap !== 'undefined' ? optSourceMap : Boolean(sourceMap),
                        });

                        if (map && sourceMap) {
                            map.applySourceMap(new SourceMapConsumer(sourceMap), file);
                        }

                        if (!map) {
                            map = sourceMap;
                        }

                        let out = new RawSource(css);

                        if (map) {
                            let isInlineUrl = isInline(devtool);
                            const inputMapComment = source.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
                            const prevSourceMap = inputMapComment && inputMapComment[1];

                            if (prevSourceMap) {
                                isInlineUrl = prevSourceMap.startsWith('data:application/json;charset=utf-8;base64');
                            }

                            if (optSourceMap === 'inline') {
                                isInlineUrl = true;
                            }

                            if (isInlineUrl) {
                                const base64 = new Buffer(map.toString()).toString('base64');
                                out = new ConcatSource(out, sourceMapURL(`data:application/json;base64,${base64}`));
                            } else {
                                const srcMapUrl = prevSourceMap || `${file}.map`; // @ToDo: compilation.getPath
                                out = new SourceMapSource(css + sourceMapURL(srcMapUrl), file, map, source, sourceMap);
                            }
                        }

                        compilation.assets[file] = out;
                    } catch (err) {
                        let error = err;
                        const prefix = `${file} from CssoWebpackPlugin\n`;
                        const { message, parseError, stack } = err;

                        if (parseError) {
                            error = `${message} [${file}:${parseError.line}:${parseError.column}]`;
                        } else {
                            error = `${message} ${stack}`;
                        }

                        compilation.errors.push(new Error(`${prefix}${error}`));
                    }

                    return callback();
                });
            });
        });
    }
}
<|endoftext|>


<commit_before>const URI = require("urijs")
const cheerio = require("cheerio")
const path = require("path")

const isHTML = filename => /\.html$/.exec(filename)

function fileExists(files, filename) {
  // Remove leading slash before checking to match the Metalsmith files format
  if (filename.charAt(0) === "/") {
    filename = filename.slice(1)
  }

  // True if it exists in the files object
  if (filename in files) {
    return true
  }

  // Fallback for Windows paths
  const winPath = filename.split("/").join(path.sep)
  return winPath in files
}

class Link {
  constructor($link) {
    if ($link.is("a")) {
      this.text = $link.text()
      this.href = $link.attr("href")
      this.isAnchor = ($link.attr("name") || $link.attr("id")) && (this.href == null)

    } else if ($link.is("img")) {
      this.text = $link.attr("alt")
      this.href = $link.attr("src")
    }
  }

  isBroken(filename, files, options) {
    // Allow anchors before checking for a missing href
    if (options.allowAnchors && this.isAnchor) {
      return false
    }

    // Missing href is always broken
    if ((this.href == null)) {
      return true
    }

    const uri = URI(this.href)

    // Allow anything matching the options.allowRegex regex
    if ((options.allowRegex != null) && options.allowRegex.exec(this.href)) {
      return false
    }

    // Empty link is always broken
    if (this.href === "") {
      return true
    }

    // Allow link to '#'
    if (this.href === "#") {
      return false
    }

    // Automatically accept all external links (could change later)
    if (uri.hostname()) {
      return false
    }

    // Ignore mailto and other non-http/https links
    if (uri.protocol() && !["http", "https"].includes(uri.protocol)) {
      return false
    }

    // Allow links to elements on the same page
    if (uri.fragment() && !uri.path()) {
      return false
    }

    // Add baseURL in here so that the linkPath resolves to it in the case of
    // a relative link
    if (options.baseURL) {
      filename = path.join(options.baseURL, filename)
    }

    // Need to transform uri.path() into something Metalsmith can recognise
    const unixFilename = filename.replace(/\\/g, "/")
    let linkPath = uri.absoluteTo(unixFilename).path()

    // If baseURL then all internal links should be prefixed by it.
    if (options.baseURL) {

      // If the linkPath does not start with the baseURL then it is broken
      if (linkPath.indexOf(options.baseURL) !== 0) {
        return true
      }

      // Strip the baseURL out for checking whether the file exists in metalsmith
      linkPath = linkPath.replace(options.baseURL, "")

      // Fix bug where you were linking directly to the linkPath
      if (linkPath === "") {
        linkPath = "/"
      }
    }

    // Special case for link to root
    if (linkPath === "/") {
      return !fileExists(files, "index.html")
    }

    // Allow links to directories with a trailing slash
    if (linkPath.slice(-1) === "/") {
      linkPath += "index.html"
    }

    // Allow links to directories without a trailing slash with allowRedirects option
    if (options.allowRedirects && fileExists(files, linkPath + "/index.html")) {
      return false
    }

    return !fileExists(files, linkPath)
  }

  toString() {
    return `href: "${this.href}", text: "${this.text}"`
  }
}

module.exports = (options) => {

  let selector
  if (options == null) { options = {} }
  if (options === true) { options = {} } // Allow CLI to specify true
  if (options.warn == null) { options.warn = false }
  if (options.checkLinks == null) { options.checkLinks = true }
  if (options.checkImages == null) { options.checkImages = true }
  if (options.allowRegex == null) { options.allowRegex = null }
  if (options.allowAnchors == null) { options.allowAnchors = true }
  if (options.allowAnchors == null) { options.allowAnchors = false }
  if (options.baseURL == null) { options.baseURL = null }

  if (options.checkLinks && options.checkImages) {
    selector = "a, img"
  } else if (options.checkLinks) {
    selector = "a"
  } else if (options.checkImages) {
    selector = "img"
  } else {
    // Check nothing so just return nop function
    return () => null
  }

  return (files) => {
    const result = []
    for (const filename in files) {

      const file = files[filename]
      if (!isHTML(filename)) { continue }

      const contents = file.contents.toString()
      const $ = cheerio.load(contents)

      result.push($(selector).each(function() {
        const link = new Link($(this))
        if (link.isBroken(filename, files, options)) {
          if (options.warn) {
            console.log(`Warning: Link is broken: ${link.toString()}, in file: ${filename}`)
          } else {
            throw new Error(`Link is broken: ${link.toString()}, in file: ${filename}`)
          }
        }
      }))
    }
    return result
  }
}
<commit_msg>Remove duplicate allowAnchors default<commit_after>const URI = require("urijs")
const cheerio = require("cheerio")
const path = require("path")

const isHTML = filename => /\.html$/.exec(filename)

function fileExists(files, filename) {
  // Remove leading slash before checking to match the Metalsmith files format
  if (filename.charAt(0) === "/") {
    filename = filename.slice(1)
  }

  // True if it exists in the files object
  if (filename in files) {
    return true
  }

  // Fallback for Windows paths
  const winPath = filename.split("/").join(path.sep)
  return winPath in files
}

class Link {
  constructor($link) {
    if ($link.is("a")) {
      this.text = $link.text()
      this.href = $link.attr("href")
      this.isAnchor = ($link.attr("name") || $link.attr("id")) && (this.href == null)

    } else if ($link.is("img")) {
      this.text = $link.attr("alt")
      this.href = $link.attr("src")
    }
  }

  isBroken(filename, files, options) {
    // Allow anchors before checking for a missing href
    if (options.allowAnchors && this.isAnchor) {
      return false
    }

    // Missing href is always broken
    if ((this.href == null)) {
      return true
    }

    const uri = URI(this.href)

    // Allow anything matching the options.allowRegex regex
    if ((options.allowRegex != null) && options.allowRegex.exec(this.href)) {
      return false
    }

    // Empty link is always broken
    if (this.href === "") {
      return true
    }

    // Allow link to '#'
    if (this.href === "#") {
      return false
    }

    // Automatically accept all external links (could change later)
    if (uri.hostname()) {
      return false
    }

    // Ignore mailto and other non-http/https links
    if (uri.protocol() && !["http", "https"].includes(uri.protocol)) {
      return false
    }

    // Allow links to elements on the same page
    if (uri.fragment() && !uri.path()) {
      return false
    }

    // Add baseURL in here so that the linkPath resolves to it in the case of
    // a relative link
    if (options.baseURL) {
      filename = path.join(options.baseURL, filename)
    }

    // Need to transform uri.path() into something Metalsmith can recognise
    const unixFilename = filename.replace(/\\/g, "/")
    let linkPath = uri.absoluteTo(unixFilename).path()

    // If baseURL then all internal links should be prefixed by it.
    if (options.baseURL) {

      // If the linkPath does not start with the baseURL then it is broken
      if (linkPath.indexOf(options.baseURL) !== 0) {
        return true
      }

      // Strip the baseURL out for checking whether the file exists in metalsmith
      linkPath = linkPath.replace(options.baseURL, "")

      // Fix bug where you were linking directly to the linkPath
      if (linkPath === "") {
        linkPath = "/"
      }
    }

    // Special case for link to root
    if (linkPath === "/") {
      return !fileExists(files, "index.html")
    }

    // Allow links to directories with a trailing slash
    if (linkPath.slice(-1) === "/") {
      linkPath += "index.html"
    }

    // Allow links to directories without a trailing slash with allowRedirects option
    if (options.allowRedirects && fileExists(files, linkPath + "/index.html")) {
      return false
    }

    return !fileExists(files, linkPath)
  }

  toString() {
    return `href: "${this.href}", text: "${this.text}"`
  }
}

module.exports = (options) => {

  let selector
  if (options == null) { options = {} }
  if (options === true) { options = {} } // Allow CLI to specify true
  if (options.warn == null) { options.warn = false }
  if (options.checkLinks == null) { options.checkLinks = true }
  if (options.checkImages == null) { options.checkImages = true }
  if (options.allowRegex == null) { options.allowRegex = null }
  if (options.allowAnchors == null) { options.allowAnchors = true }
  if (options.baseURL == null) { options.baseURL = null }

  if (options.checkLinks && options.checkImages) {
    selector = "a, img"
  } else if (options.checkLinks) {
    selector = "a"
  } else if (options.checkImages) {
    selector = "img"
  } else {
    // Check nothing so just return nop function
    return () => null
  }

  return (files) => {
    const result = []
    for (const filename in files) {

      const file = files[filename]
      if (!isHTML(filename)) { continue }

      const contents = file.contents.toString()
      const $ = cheerio.load(contents)

      result.push($(selector).each(function() {
        const link = new Link($(this))
        if (link.isBroken(filename, files, options)) {
          if (options.warn) {
            console.log(`Warning: Link is broken: ${link.toString()}, in file: ${filename}`)
          } else {
            throw new Error(`Link is broken: ${link.toString()}, in file: ${filename}`)
          }
        }
      }))
    }
    return result
  }
}
<|endoftext|>


<commit_before>import template from 'babel-template';

const buildClassDecorator = template(`
  DECORATOR(CLASS_REF = INNER) || CLASS_REF;
`);

const buildPropertyDecorator = template(`
  DESC = (DECORATOR(TARGET, PROPERTY, DESC = INNER) || DESC);
`);

const buildClassPrototype = template(`
  CLASS_REF.prototype;
`);

const buildGetDescriptor = template(`
    Object.getOwnPropertyDescriptor(TARGET, PROPERTY);
`);

const buildSetDescriptor = template(`
    DESC ? Object.defineProperty(TARGET, PROPERTY, DESC) : undefined;
`);

const buildGetObjectInitializer = template(`
    (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY).value, {
        enumerable: true,
        configurable: true,
        writable: true,
        initializer: function(){
            return TEMP;
        }
    })
`);

const buildSetObjectInitializer = template(`
    (DESC.value = DESC.initializer ? DESC.initializer.call(TARGET) : undefined) &&
        Object.defineProperty(TARGET, PROPERTY, DESC);
`);

const buildGetClassInitializer = template(`
    ({
        enumerable: true,
        configurable: true,
        writable: true,
        initializer: INIT,
    })
`);

const buildSetClassInitializer = template(`
    INIT = DESC.initializer;
`);

export default function({types: t}){

    /**
     * Given a class expression with class-level decorators, create a new expression
     * with the proper decorated behavior.
     */
    function applyClassDecorators(classPath){
        const decorators = classPath.node.decorators || [];
        classPath.node.decorators = null;

        if (decorators.length === 0) return;

        const name = classPath.scope.generateDeclaredUidIdentifier('class');

        return decorators
            .map(dec => dec.expression)
            .reverse()
            .reduce(function(acc, decorator){
                return buildClassDecorator({
                    CLASS_REF: name,
                    DECORATOR: decorator,
                    INNER: acc,
                }).expression;
            }, classPath.node);
    }

    /**
     * Given a class expression with method-level decorators, create a new expression
     * with the proper decorated behavior.
     */
    function applyMethodDecorators(classPath){
        const hasMethodDecorators = classPath.node.body.body.some(function(node){
            return (node.decorators || []).length > 0;
        });

        if (!hasMethodDecorators) return;

        const name = classPath.scope.generateDeclaredUidIdentifier('class');

        const target = buildClassPrototype({
            CLASS_REF: name,
        }).expression;

        return t.sequenceExpression([
            t.assignmentExpression('=', name, classPath.node),
            applyTargetDecorators(classPath, name, target, classPath.node.body.body),
            name,
        ]);
    }

    /**
     * Given an object expression with property decorators, create a new expression
     * with the proper decorated behavior.
     */
    function applyObjectDecorators(path){
        const hasMethodDecorators = path.node.properties.some(function(node){
            return (node.decorators || []).length > 0;
        });

        if (!hasMethodDecorators) return;

        const name = path.scope.generateDeclaredUidIdentifier('obj');

        return t.sequenceExpression([
            t.assignmentExpression('=', name, path.node),
            applyTargetDecorators(path, name, name, path.node.properties),
            name,
        ]);
    }

    /**
     * A helper to pull out property decorators into a sequence expression.
     */
    function applyTargetDecorators(path, name, target, decoratedProps){
        const descName = path.scope.generateDeclaredUidIdentifier('desc');
        const valueTemp = path.scope.generateDeclaredUidIdentifier('value');

        return t.sequenceExpression(decoratedProps.reduce(function(acc, node){
            const decorators = node.decorators || [];
            node.decorators = null;

            if (decorators.length === 0) return acc;

            const property = t.stringLiteral(node.key.name);

            if (t.isObjectProperty(node)){
                const init = path.scope.generateDeclaredUidIdentifier('init');

                return acc.concat(
                    decorators
                        .map(dec => dec.expression)
                        .reverse()
                        .reduce(function(descriptor, expr){
                            return buildPropertyDecorator({
                                TARGET: target,
                                DECORATOR: expr,
                                DESC: descName,
                                INNER: descriptor,
                                PROPERTY: property,
                            }).expression;
                        }, buildGetObjectInitializer({
                            TEMP: init,
                            TARGET: target,
                            PROPERTY: property
                        }).expression),
                    buildSetObjectInitializer({
                        TARGET: target,
                        PROPERTY: property,
                        DESC: descName,
                    }).expression
                );
            } else if (t.isClassProperty(node)){
                let init = path.scope.generateDeclaredUidIdentifier('init');
                let oldInitializer;
                if (node.value){
                    oldInitializer = node.value;

                    acc.push(t.assignmentExpression('=', init, t.functionExpression(null, [], t.blockStatement([
                        t.returnStatement(oldInitializer),
                    ]))));
                }
                node.value = t.callExpression(t.memberExpression(init, t.identifier('apply')), [t.thisExpression()]);

                return acc.concat(
                    decorators
                        .map(dec => dec.expression)
                        .reverse()
                        .reduce(function(descriptor, expr){
                            return buildPropertyDecorator({
                                TARGET: target,
                                DECORATOR: expr,
                                DESC: descName,
                                INNER: descriptor,
                                PROPERTY: property,
                            }).expression;
                        }, buildGetClassInitializer({
                            INIT: oldInitializer ? init : t.nullLiteral(),
                        }).expression),
                    buildSetClassInitializer({
                        INIT: init,
                        DESC: descName,
                    }).expression
                );
            } else {
                return acc.concat(
                    decorators
                        .map(dec => dec.expression)
                        .reverse()
                        .reduce(function(descriptor, expr){
                            return buildPropertyDecorator({
                                TARGET: target,
                                DECORATOR: expr,
                                DESC: descName,
                                INNER: descriptor,
                                PROPERTY: property,
                            }).expression;
                        }, buildGetDescriptor({
                            TARGET: target,
                            PROPERTY: property,
                        }).expression),
                    buildSetDescriptor({
                        TARGET: target,
                        PROPERTY: property,
                        DESC: descName,
                    }).expression
                );
            }
        }, []))
    }

    return {
        inherits: require("babel-plugin-syntax-decorators"),

        visitor: {
            ExportDefaultDeclaration(path){
                if (!path.get("declaration").isClassDeclaration()) return;

                const {node} = path;
                const ref = node.declaration.id || path.scope.generateUidIdentifier("default");
                node.declaration.id = ref;

                // Split the class declaration and the export into two separate statements.
                path.replaceWith(node.declaration);
                path.insertAfter(t.exportNamedDeclaration(null, [t.exportSpecifier(ref, t.identifier('default'))]));
            },
            ClassDeclaration(path){
                const {node} = path;

                const ref = node.id || path.scope.generateUidIdentifier("class");

                path.replaceWith(t.variableDeclaration("let", [
                  t.variableDeclarator(ref, t.toExpression(node))
                ]));
            },
            ClassExpression(path){
                // Create a replacement for the class node if there is one. We do one pass to replace classes with
                // class decorators, and a second pass to process method decorators.
                const decoratedClass = applyClassDecorators(path) || applyMethodDecorators(path);

                if (decoratedClass) path.replaceWith(decoratedClass);
            },
            ObjectExpression(path){
                const decoratedObject = applyObjectDecorators(path);

                if (decoratedObject) path.replaceWith(decoratedObject);
            },
        }
    };
};
<commit_msg>Revert change from PR for now.<commit_after>import template from 'babel-template';

const buildClassDecorator = template(`
  DECORATOR(CLASS_REF = INNER) || CLASS_REF;
`);

const buildPropertyDecorator = template(`
  DECORATOR(TARGET, PROPERTY, DESC = INNER) || DESC;
`);

const buildClassPrototype = template(`
  CLASS_REF.prototype;
`);

const buildGetDescriptor = template(`
    Object.getOwnPropertyDescriptor(TARGET, PROPERTY);
`);

const buildSetDescriptor = template(`
    DESC ? Object.defineProperty(TARGET, PROPERTY, DESC) : undefined;
`);

const buildGetObjectInitializer = template(`
    (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY).value, {
        enumerable: true,
        configurable: true,
        writable: true,
        initializer: function(){
            return TEMP;
        }
    })
`);

const buildSetObjectInitializer = template(`
    (DESC.value = DESC.initializer ? DESC.initializer.call(TARGET) : undefined) &&
        Object.defineProperty(TARGET, PROPERTY, DESC);
`);

const buildGetClassInitializer = template(`
    ({
        enumerable: true,
        configurable: true,
        writable: true,
        initializer: INIT,
    })
`);

const buildSetClassInitializer = template(`
    INIT = DESC.initializer;
`);

export default function({types: t}){

    /**
     * Given a class expression with class-level decorators, create a new expression
     * with the proper decorated behavior.
     */
    function applyClassDecorators(classPath){
        const decorators = classPath.node.decorators || [];
        classPath.node.decorators = null;

        if (decorators.length === 0) return;

        const name = classPath.scope.generateDeclaredUidIdentifier('class');

        return decorators
            .map(dec => dec.expression)
            .reverse()
            .reduce(function(acc, decorator){
                return buildClassDecorator({
                    CLASS_REF: name,
                    DECORATOR: decorator,
                    INNER: acc,
                }).expression;
            }, classPath.node);
    }

    /**
     * Given a class expression with method-level decorators, create a new expression
     * with the proper decorated behavior.
     */
    function applyMethodDecorators(classPath){
        const hasMethodDecorators = classPath.node.body.body.some(function(node){
            return (node.decorators || []).length > 0;
        });

        if (!hasMethodDecorators) return;

        const name = classPath.scope.generateDeclaredUidIdentifier('class');

        const target = buildClassPrototype({
            CLASS_REF: name,
        }).expression;

        return t.sequenceExpression([
            t.assignmentExpression('=', name, classPath.node),
            applyTargetDecorators(classPath, name, target, classPath.node.body.body),
            name,
        ]);
    }

    /**
     * Given an object expression with property decorators, create a new expression
     * with the proper decorated behavior.
     */
    function applyObjectDecorators(path){
        const hasMethodDecorators = path.node.properties.some(function(node){
            return (node.decorators || []).length > 0;
        });

        if (!hasMethodDecorators) return;

        const name = path.scope.generateDeclaredUidIdentifier('obj');

        return t.sequenceExpression([
            t.assignmentExpression('=', name, path.node),
            applyTargetDecorators(path, name, name, path.node.properties),
            name,
        ]);
    }

    /**
     * A helper to pull out property decorators into a sequence expression.
     */
    function applyTargetDecorators(path, name, target, decoratedProps){
        const descName = path.scope.generateDeclaredUidIdentifier('desc');
        const valueTemp = path.scope.generateDeclaredUidIdentifier('value');

        return t.sequenceExpression(decoratedProps.reduce(function(acc, node){
            const decorators = node.decorators || [];
            node.decorators = null;

            if (decorators.length === 0) return acc;

            const property = t.stringLiteral(node.key.name);

            if (t.isObjectProperty(node)){
                const init = path.scope.generateDeclaredUidIdentifier('init');

                return acc.concat(
                    decorators
                        .map(dec => dec.expression)
                        .reverse()
                        .reduce(function(descriptor, expr){
                            return buildPropertyDecorator({
                                TARGET: target,
                                DECORATOR: expr,
                                DESC: descName,
                                INNER: descriptor,
                                PROPERTY: property,
                            }).expression;
                        }, buildGetObjectInitializer({
                            TEMP: init,
                            TARGET: target,
                            PROPERTY: property
                        }).expression),
                    buildSetObjectInitializer({
                        TARGET: target,
                        PROPERTY: property,
                        DESC: descName,
                    }).expression
                );
            } else if (t.isClassProperty(node)){
                let init = path.scope.generateDeclaredUidIdentifier('init');
                let oldInitializer;
                if (node.value){
                    oldInitializer = node.value;

                    acc.push(t.assignmentExpression('=', init, t.functionExpression(null, [], t.blockStatement([
                        t.returnStatement(oldInitializer),
                    ]))));
                }
                node.value = t.callExpression(t.memberExpression(init, t.identifier('apply')), [t.thisExpression()]);

                return acc.concat(
                    decorators
                        .map(dec => dec.expression)
                        .reverse()
                        .reduce(function(descriptor, expr){
                            return buildPropertyDecorator({
                                TARGET: target,
                                DECORATOR: expr,
                                DESC: descName,
                                INNER: descriptor,
                                PROPERTY: property,
                            }).expression;
                        }, buildGetClassInitializer({
                            INIT: oldInitializer ? init : t.nullLiteral(),
                        }).expression),
                    buildSetClassInitializer({
                        INIT: init,
                        DESC: descName,
                    }).expression
                );
            } else {
                return acc.concat(
                    decorators
                        .map(dec => dec.expression)
                        .reverse()
                        .reduce(function(descriptor, expr){
                            return buildPropertyDecorator({
                                TARGET: target,
                                DECORATOR: expr,
                                DESC: descName,
                                INNER: descriptor,
                                PROPERTY: property,
                            }).expression;
                        }, buildGetDescriptor({
                            TARGET: target,
                            PROPERTY: property,
                        }).expression),
                    buildSetDescriptor({
                        TARGET: target,
                        PROPERTY: property,
                        DESC: descName,
                    }).expression
                );
            }
        }, []))
    }

    return {
        inherits: require("babel-plugin-syntax-decorators"),

        visitor: {
            ExportDefaultDeclaration(path){
                if (!path.get("declaration").isClassDeclaration()) return;

                const {node} = path;
                const ref = node.declaration.id || path.scope.generateUidIdentifier("default");
                node.declaration.id = ref;

                // Split the class declaration and the export into two separate statements.
                path.replaceWith(node.declaration);
                path.insertAfter(t.exportNamedDeclaration(null, [t.exportSpecifier(ref, t.identifier('default'))]));
            },
            ClassDeclaration(path){
                const {node} = path;

                const ref = node.id || path.scope.generateUidIdentifier("class");

                path.replaceWith(t.variableDeclaration("let", [
                  t.variableDeclarator(ref, t.toExpression(node))
                ]));
            },
            ClassExpression(path){
                // Create a replacement for the class node if there is one. We do one pass to replace classes with
                // class decorators, and a second pass to process method decorators.
                const decoratedClass = applyClassDecorators(path) || applyMethodDecorators(path);

                if (decoratedClass) path.replaceWith(decoratedClass);
            },
            ObjectExpression(path){
                const decoratedObject = applyObjectDecorators(path);

                if (decoratedObject) path.replaceWith(decoratedObject);
            },
        }
    };
};
<|endoftext|>


<commit_before><commit_msg>Send message to GUI on object drag<commit_after><|endoftext|>


<commit_before>/**
 * @author Mino Togna
 */

function ScatterPlot(container) {
	// ui container
	this.container = container;

	// options section
	var optionsSection = this.container.find('.options');
	this.xCombo = optionsSection.find('[name=x]').combobox();
	this.yCombo = optionsSection.find('[name=y]').combobox();
	this.refreshBtn = optionsSection.find('[name=refresh]');

	// progress for data loading
	this.progressBar = new ProgressBar( optionsSection.find(".progress") , optionsSection.find(".percent") );

	//chart container
	this.chartContainer = this.container.find(".chart");
//	this.chartContainer = this.container.find(".chart").replaceWith( $("
</div") );
	this.chartContainer.attr('id', 'scatter-chart');
	this.chart = null;

	// data provider
	this.dataProvider = null;

	this.xVariable = null;
	this.yVariable = null;
	this.offset = 0;
	this.totalItems = -1;

	// TODO move out
	this.chartinfo = {
		    chart: {
		    	renderTo: this.chartContainer.attr("id"),
		    	type: 'scatter',
                zoomType: 'xy',
                borderWidth: 0,
                borderRadius: 0,
                backgroundColor: null,
		    },
		    credits: {
		    	enabled:false
	    	},
		    title: {
		        text: '',
		        style: {
		        	color: '#496d7e',
		        	font: '1.2em Trebuchet MS, Verdana, sans-serif'
		        }
		    },
		    xAxis: {
                title: {
                    enabled: true,
                    text: '',
    		        style: {
    		        	color: '#496d7e',
    		        	font: '1.4em Trebuchet MS, Verdana, sans-serif'
    		        }
                },
                lineWidth: 1,
                lineColor: "#ecf0f1",
                gridLineColor: "#ecf0f1",
                tickColor: "#ecf0f1",
                labels: {
                    style: {
                       color: '#ecf0f1',
                       font: '0.9em Trebuchet MS, Verdana, sans-serif'
                    }
                 },
                 events: {
                 	afterSetExtremes : function (e) {

                 	    var url;
                 	    var currentExtremes = this.getExtremes();
                 	    var range = e.max - e.min;
                 	    console.log("after set extrems: ", currentExtremes);
//                 	    var chart = $('#container').highcharts();
//
//                 	    var min = 0;
//                 	    var max = 1.35e12;
//                 	    if(!isReset)
//                 	    {
//                 	        min = e.min;
//                 	        max = e.max;
//                 	    }
//                 	     chart.showLoading('Loading data from server...');
//                 	    $.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(min) +
//                 	        '&end=' + Math.round(max) + '&callback=?', function (data) {
//
//                 	        chart.series[0].setData(data);
//
//                 	        chart.hideLoading();
//
//
//                 	    });

                 	},
                 	setExtremes: function (e) {
                 		console.log("setExtremes ", e);

                     if (e.max == null || e.min == null) {
                        isReset = true;
                     }
                     else
                     {
                      isReset = false;
                     }
                 }
             }
//                startOnTick: true,
//                endOnTick: true,
//                showLastLabel: true
            },
            yAxis: {
                title: {
                    text: '',
    		        style: {
    		        	color: '#496d7e',
    		        	font: '1.4em Trebuchet MS, Verdana, sans-serif'
    		        }
                }
            ,
//                lineWidth: 1,
//                lineColor: "#ecf0f1"
            	gridLineColor: "#ecf0f1",
            	labels: {
                    style: {
                       color: '#ecf0f1',
                       font: '0.9em Trebuchet MS, Verdana, sans-serif'
                    }
                 }
            },
            legend: {
                layout: 'vertical',
                align: 'left',
                verticalAlign: 'top',
                x: 100,
                y: 70,
                floating: true,
                backgroundColor: '#FFFFFF',
                borderWidth: 1
            },
            plotOptions: {
                scatter: {
                	color:"ecf0f1",
                    marker: {
                        radius: 5,
                        states: {
                            hover: {
                                enabled: true,
                                lineColor: 'rgb(100,100,100)'
                            }
                        }
                    },
                    states: {
                        hover: {
                            marker: {
                                enabled: false
                            }
                        }
                    },
                    tooltip: {
//                        headerFormat: '{series.name}
',
                        pointFormat: '{point.x} , {point.y}'
                    }
                }
            	,
            	series : {
            		turboThreshold : 0
            	}
            },
            series: [{
                name: '',
                color: 'rgba(108, 159, 183, .5)',
                data: []
            }]
		};

	this.workspaceManager = WorkspaceManager.getInstance();

	this._init();

};

ScatterPlot.prototype = (function(){

	// init
	var init = function() {
		var $this = this;
		//event handlers
		//change x and y variables
		$this.xCombo.change(function(e) {
			e.preventDefault();
			$this.xVariable = $this.xCombo.val();
		});
		$this.yCombo.change(function(e){
			e.preventDefault();
			$this.yVariable = $this.yCombo.val();
		});

		//refresh chart button
		$this.refreshBtn.click(function(e){
			e.preventDefault();
			if( $this.xVariable == null || $this.yVariable == null ){
				UI.showError( "x and y must be set", true );
			} else {
				$.proxy(start , $this)();
			}
		});
	};

	// set data provider and reset data table
	var setDataProvider = function(dataProvider) {
		this.dataProvider = dataProvider;
		$.proxy(reset, this)();
	};

	var reset = function() {
		var $this = this;
		// reset counters
		this.offset = 0;
		this.totalItems = -1;

		this.xVariable = null;
		this.yVariable = null;

		this.workspaceManager.activeWorkspace(function(ws) {
			var entity = ws.getEntityById($this.dataProvider.entityId);
			var variables = entity.quantitativeVariables;

			$this.xCombo.data(variables, 'name', 'name');
			$this.yCombo.data(variables, 'name', 'name');
		});

		// reset options
		this.xCombo.val("");
		this.yCombo.val("");

		// reset chart
		$.proxy(resetChart, this)();
	};

	// reset chart
	var resetChart = function(){
		if( this.chart ) {
			this.chart.destroy();
			this.chart = null;
		}
	};

	// show / hide functions
	var show = function() {
		this.container.fadeIn();

		// if it hasn't started yet and there are 2 variables to show, then it starts
		if ( this.dataProvider.variables.length == 2 && this.totalItems < 0) {
			// update combo boxes
			var xVar = this.dataProvider.variables[0];
			var yVar = this.dataProvider.variables[1];
			this.xVariable = xVar;
			this.yVariable = yVar;
			this.xCombo.val(xVar);
			this.yCombo.val(yVar);

			$.proxy(start, this)();
		}
	};

	var hide = function(){
		this.container.hide();
	};

	// private function that starts showing the data
	var start = function() {
		var $this = this;
		// count total items
		this.dataProvider.count(function(cnt){
			// reset chart
			$.proxy(resetChart, $this)();

			// set total items
			$this.totalItems = cnt;

			// create the chart
			$this.chartinfo.xAxis.title.text = this.xVariable;
			$this.chartinfo.yAxis.title.text = this.yVariable;
//			$this.chart = new Highcharts.Chart($this.chartinfo);
			// reset progress bar
			$this.progressBar.reset();
			// starts getting data next data
			$.proxy(getNextData, $this)();
		});
	};

	// get next available data from data provider and add them to the chart
	var getNextData = function() {
		var $this = this;
		if( this.xVariable != null && this.yVariable != null ) {
//			console.log("upd chart");
//			console.log(this);
			var vars = [this.xVariable, this.yVariable];
			this.dataProvider.data( $this.offset , 5000 , null , vars, function(response) {
				$.proxy(addPoints, $this)(response);

				//get next data if not all data have been loaded
//				if( $this.offset < $this.totalItems ){
//					$.proxy(getNextData, $this)();
//				}
			});
		}
	};

	// add points to the chart
	var addPoints = function(data){
		var $this = this;

		var series = $this.chartinfo.series[0];
		var seriesData = series.data;
		// prepare chart data
		$.each(data,function(i,record){
			// get x and y values from record.fields
			var seriesItem = [ record.fields[$this.xVariable] , record.fields[$this.yVariable] ] ;

			seriesData.push( seriesItem );

			//disabled now
//			$this.chart.series[0].addPoint(seriesItem, false);
//			if($this.chart.series[0].data.length % 10000 == 0) {
//				$this.chart.redraw();
//			}
		});
//		console.log($this.chart);
		// redraw chart
//		$this.chart.redraw();
		$this.chart = new Highcharts.Chart($this.chartinfo);

		// update offset
//		$this.offset = $this.chart.series[0].data.length;
		$this.offset += data.length;
		// update progress
		$this.progressBar.update($this.offset , $this.totalItems);

	};

	var d3test = function(){
		console.log("==== start testing d3");
//		var w = this.chartContainer.width();
//		var h = this.chartContainer.height();
		var w = 900;
		var h = 1000;

		var dataset = seriesData;
//		               var dataset = [
//						[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
//						[410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
//					  ];

		//Create SVG element
		var svg = d3.select("#" + this.chartContainer.attr('id') )
					.append("svg")
					.attr("width", w)
					.attr("height", h);

		svg.selectAll("circle")
		   .data(dataset)
		   .enter()
		   .append("circle")
		   .attr("cx", function(d) {
		   		return d[0] * 100 / 900;
		   })
		   .attr("cy", function(d) {
			   	return d[1] * 100 / 1000;
//		   		return d[1];
		   })
		   .attr("r", function(d) {
		   		return 1;
		   });
//		   .attr("r", function(d) {
//		   		return Math.sqrt(h - d[1]);
//		   });

//		svg.selectAll("text")
//		   .data(dataset)
//		   .enter()
//		   .append("text")
//		   .text(function(d) {
//		   		return d[0] + "," + d[1];
//		   })
//		   .attr("x", function(d) {
//		   		return d[0];
//		   })
//		   .attr("y", function(d) {
//		   		return d[1];
//		   })
//		   .attr("font-family", "sans-serif")
//		   .attr("font-size", "11px")
//		   .attr("fill", "red");
	};

	return {
		constructor : ScatterPlot,

		_init : init,

		setDataProvider : setDataProvider,

		show : show ,

		hide : hide

	};
})();
<commit_msg>Reset data provider offset when refreshing scatter plot<commit_after>/**
 * @author Mino Togna
 */

function ScatterPlot(container) {
	// ui container
	this.container = container;

	// options section
	var optionsSection = this.container.find('.options');
	this.xCombo = optionsSection.find('[name=x]').combobox();
	this.yCombo = optionsSection.find('[name=y]').combobox();
	this.refreshBtn = optionsSection.find('[name=refresh]');

	// progress for data loading
	this.progressBar = new ProgressBar( optionsSection.find(".progress") , optionsSection.find(".percent") );

	//chart container
	this.chartContainer = this.container.find(".chart");
//	this.chartContainer = this.container.find(".chart").replaceWith( $("
</div") );
	this.chartContainer.attr('id', 'scatter-chart');
	this.chart = null;

	// data provider
	this.dataProvider = null;

	this.xVariable = null;
	this.yVariable = null;
	this.offset = 0;
	this.totalItems = -1;

	// TODO move out
	this.chartinfo = {
		    chart: {
		    	renderTo: this.chartContainer.attr("id"),
		    	type: 'scatter',
                zoomType: 'xy',
                borderWidth: 0,
                borderRadius: 0,
                backgroundColor: null,
		    },
		    credits: {
		    	enabled:false
	    	},
		    title: {
		        text: '',
		        style: {
		        	color: '#496d7e',
		        	font: '1.2em Trebuchet MS, Verdana, sans-serif'
		        }
		    },
		    xAxis: {
                title: {
                    enabled: true,
                    text: '',
    		        style: {
    		        	color: '#496d7e',
    		        	font: '1.4em Trebuchet MS, Verdana, sans-serif'
    		        }
                },
                lineWidth: 1,
                lineColor: "#ecf0f1",
                gridLineColor: "#ecf0f1",
                tickColor: "#ecf0f1",
                labels: {
                    style: {
                       color: '#ecf0f1',
                       font: '0.9em Trebuchet MS, Verdana, sans-serif'
                    }
                 },
                 events: {
                 	afterSetExtremes : function (e) {

                 	    var url;
                 	    var currentExtremes = this.getExtremes();
                 	    var range = e.max - e.min;
                 	    console.log("after set extrems: ", currentExtremes);
//                 	    var chart = $('#container').highcharts();
//
//                 	    var min = 0;
//                 	    var max = 1.35e12;
//                 	    if(!isReset)
//                 	    {
//                 	        min = e.min;
//                 	        max = e.max;
//                 	    }
//                 	     chart.showLoading('Loading data from server...');
//                 	    $.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(min) +
//                 	        '&end=' + Math.round(max) + '&callback=?', function (data) {
//
//                 	        chart.series[0].setData(data);
//
//                 	        chart.hideLoading();
//
//
//                 	    });

                 	},
                 	setExtremes: function (e) {
                 		console.log("setExtremes ", e);

                     if (e.max == null || e.min == null) {
                        isReset = true;
                     }
                     else
                     {
                      isReset = false;
                     }
                 }
             }
//                startOnTick: true,
//                endOnTick: true,
//                showLastLabel: true
            },
            yAxis: {
                title: {
                    text: '',
    		        style: {
    		        	color: '#496d7e',
    		        	font: '1.4em Trebuchet MS, Verdana, sans-serif'
    		        }
                }
            ,
//                lineWidth: 1,
//                lineColor: "#ecf0f1"
            	gridLineColor: "#ecf0f1",
            	labels: {
                    style: {
                       color: '#ecf0f1',
                       font: '0.9em Trebuchet MS, Verdana, sans-serif'
                    }
                 }
            },
            legend: {
                layout: 'vertical',
                align: 'left',
                verticalAlign: 'top',
                x: 100,
                y: 70,
                floating: true,
                backgroundColor: '#FFFFFF',
                borderWidth: 1
            },
            plotOptions: {
                scatter: {
                	color:"ecf0f1",
                    marker: {
                        radius: 5,
                        states: {
                            hover: {
                                enabled: true,
                                lineColor: 'rgb(100,100,100)'
                            }
                        }
                    },
                    states: {
                        hover: {
                            marker: {
                                enabled: false
                            }
                        }
                    },
                    tooltip: {
//                        headerFormat: '{series.name}
',
                        pointFormat: '{point.x} , {point.y}'
                    }
                }
            	,
            	series : {
            		turboThreshold : 0
            	}
            },
            series: [{
                name: '',
                color: 'rgba(108, 159, 183, .5)',
                data: []
            }]
		};

	this.workspaceManager = WorkspaceManager.getInstance();

	this._init();

};

ScatterPlot.prototype = (function(){

	// init
	var init = function() {
		var $this = this;
		//event handlers
		//change x and y variables
		$this.xCombo.change(function(e) {
			e.preventDefault();
			$this.xVariable = $this.xCombo.val();
		});
		$this.yCombo.change(function(e){
			e.preventDefault();
			$this.yVariable = $this.yCombo.val();
		});

		//refresh chart button
		$this.refreshBtn.click(function(e){
			e.preventDefault();
			if( $this.xVariable == null || $this.yVariable == null ){
				UI.showError( "x and y must be set", true );
			} else {
				$.proxy(start , $this)();
			}
		});
	};

	// set data provider and reset data table
	var setDataProvider = function(dataProvider) {
		this.dataProvider = dataProvider;
		$.proxy(reset, this)();
	};

	var reset = function() {
		var $this = this;
		// reset counters
		this.offset = 0;
		this.totalItems = -1;

		this.xVariable = null;
		this.yVariable = null;

		this.workspaceManager.activeWorkspace(function(ws) {
			var entity = ws.getEntityById($this.dataProvider.entityId);
			var variables = entity.quantitativeVariables;

			$this.xCombo.data(variables, 'name', 'name');
			$this.yCombo.data(variables, 'name', 'name');
		});

		// reset options
		this.xCombo.val("");
		this.yCombo.val("");

		// reset chart
		$.proxy(resetChart, this)();
	};

	// reset chart
	var resetChart = function(){
		if( this.chart ) {
			this.chart.destroy();
			this.chart = null;
		}
	};

	// show / hide functions
	var show = function() {
		this.container.fadeIn();

		// if it hasn't started yet and there are 2 variables to show, then it starts
		if ( this.dataProvider.variables.length == 2 && this.totalItems < 0) {
			// update combo boxes
			var xVar = this.dataProvider.variables[0];
			var yVar = this.dataProvider.variables[1];
			this.xVariable = xVar;
			this.yVariable = yVar;
			this.xCombo.val(xVar);
			this.yCombo.val(yVar);

			$.proxy(start, this)();
		}
	};

	var hide = function(){
		this.container.hide();
	};

	// private function that starts showing the data
	var start = function() {
		var $this = this;
		// count total items
		this.dataProvider.count(function(cnt){
			// reset chart
			$.proxy(resetChart, $this)();

			// set total items
			$this.totalItems = cnt;
			$this.offset = 0;

			// create the chart
			$this.chartinfo.xAxis.title.text = this.xVariable;
			$this.chartinfo.yAxis.title.text = this.yVariable;
//			$this.chart = new Highcharts.Chart($this.chartinfo);
			// reset progress bar
			$this.progressBar.reset();
			// starts getting data next data
			$.proxy(getNextData, $this)();
		});
	};

	// get next available data from data provider and add them to the chart
	var getNextData = function() {
		var $this = this;
		if( this.xVariable != null && this.yVariable != null ) {
//			console.log("upd chart");
//			console.log(this);
			var vars = [this.xVariable, this.yVariable];
			this.dataProvider.data( $this.offset , 5000 , null , vars, function(response) {
				$.proxy(addPoints, $this)(response);

				//get next data if not all data have been loaded
//				if( $this.offset < $this.totalItems ){
//					$.proxy(getNextData, $this)();
//				}
			});
		}
	};

	// add points to the chart
	var addPoints = function(data){
		var $this = this;

		var series = $this.chartinfo.series[0];
		var seriesData = series.data;
		// prepare chart data
		$.each(data,function(i,record){
			// get x and y values from record.fields
			var seriesItem = [ record.fields[$this.xVariable] , record.fields[$this.yVariable] ] ;

			seriesData.push( seriesItem );

			//disabled now
//			$this.chart.series[0].addPoint(seriesItem, false);
//			if($this.chart.series[0].data.length % 10000 == 0) {
//				$this.chart.redraw();
//			}
		});
//		console.log($this.chart);
		// redraw chart
//		$this.chart.redraw();
		$this.chart = new Highcharts.Chart($this.chartinfo);

		// update offset
//		$this.offset = $this.chart.series[0].data.length;
		$this.offset += data.length;
		// update progress
		$this.progressBar.update($this.offset , $this.totalItems);

	};

	var d3test = function(){
		console.log("==== start testing d3");
//		var w = this.chartContainer.width();
//		var h = this.chartContainer.height();
		var w = 900;
		var h = 1000;

		var dataset = seriesData;
//		               var dataset = [
//						[5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
//						[410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
//					  ];

		//Create SVG element
		var svg = d3.select("#" + this.chartContainer.attr('id') )
					.append("svg")
					.attr("width", w)
					.attr("height", h);

		svg.selectAll("circle")
		   .data(dataset)
		   .enter()
		   .append("circle")
		   .attr("cx", function(d) {
		   		return d[0] * 100 / 900;
		   })
		   .attr("cy", function(d) {
			   	return d[1] * 100 / 1000;
//		   		return d[1];
		   })
		   .attr("r", function(d) {
		   		return 1;
		   });
//		   .attr("r", function(d) {
//		   		return Math.sqrt(h - d[1]);
//		   });

//		svg.selectAll("text")
//		   .data(dataset)
//		   .enter()
//		   .append("text")
//		   .text(function(d) {
//		   		return d[0] + "," + d[1];
//		   })
//		   .attr("x", function(d) {
//		   		return d[0];
//		   })
//		   .attr("y", function(d) {
//		   		return d[1];
//		   })
//		   .attr("font-family", "sans-serif")
//		   .attr("font-size", "11px")
//		   .attr("fill", "red");
	};

	return {
		constructor : ScatterPlot,

		_init : init,

		setDataProvider : setDataProvider,

		show : show ,

		hide : hide

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


<commit_before>'use strict';

define(
  [
    'flight/lib/component'
  ],

  function (defineComponent) {

    return defineComponent(timeStamp);

    function timeStamp() {
      this.init = function () {
        this.$timestamp = this.$node.find("[name=timestamp]");
        this.$date = this.$node.find(".date-input");
        this.$time = this.$node.find(".time-input");
        var ts = this.$timestamp.val();
        this.setDateTime((ts) ? moment(ts / 1000) : moment());
      }

      this.setDateTime = function(time) {
        this.$date.val(time.format("DD-MM-YYYY"));
        this.$time.val(time.format("HH:mm"));
      }

      this.setTimestamp = function(time) {
        this.$timestamp.val(time.valueOf() * 1000);
      }

      this.dateChanged = function (e) {
        this.setTimestamp(moment(e.date));
      }

      this.timeChanged = function () {
        var time = moment(this.$date.val(), "MM-DD-YYYY");
        time.add(moment.duration(this.$time.val()));
        this.setTimestamp(moment.utc(time));
      }

      this.after('initialize', function () {
        this.init();
        this.on(this.$time, "change", this.timeChanged);
        this.$date
          .datepicker({format: 'mm-dd-yyyy'})
          .on("changeDate", this.dateChanged.bind(this));
      });
    }
  }
);
<commit_msg>adjust time format to be dd-mm-yyyy<commit_after>'use strict';

define(
  [
    'flight/lib/component'
  ],

  function (defineComponent) {

    return defineComponent(timeStamp);

    function timeStamp() {
      this.init = function () {
        this.$timestamp = this.$node.find("[name=timestamp]");
        this.$date = this.$node.find(".date-input");
        this.$time = this.$node.find(".time-input");
        var ts = this.$timestamp.val();
        this.setDateTime((ts) ? moment(ts / 1000) : moment());
      }

      this.setDateTime = function(time) {
        this.$date.val(time.format("DD-MM-YYYY"));
        this.$time.val(time.format("HH:mm"));
      }

      this.setTimestamp = function(time) {
        this.$timestamp.val(time.valueOf() * 1000);
      }

      this.dateChanged = function (e) {
        this.setTimestamp(moment(e.date));
      }

      this.timeChanged = function () {
        var time = moment(this.$date.val(), "DD-MM-YYYY");
        time.add(moment.duration(this.$time.val()));
        this.setTimestamp(moment.utc(time));
      }

      this.after('initialize', function () {
        this.init();
        this.on(this.$time, "change", this.timeChanged);
        this.$date
          .datepicker({format: 'dd-mm-yyyy'})
          .on("changeDate", this.dateChanged.bind(this));
      });
    }
  }
);
<|endoftext|>


<commit_before>import Promise from 'bluebird';
import { Client, types as CassandraTypes } from 'cassandra-driver';
import { lookupServiceAsync } from './service-discovery';
import { withRetries } from './with-retries';
import { logger } from './logging';

/**
 * Looks up the location of the cassandra service and creates a client with the options/keyspace
 * specified. Returns a Promise of the created client.
 */
function createClientAsync(keyspace, queryOptions) {
  return lookupServiceAsync('cassandra')
    .then(contactPoints => {
      let client = new Client({ contactPoints, keyspace, queryOptions });
      Promise.promisifyAll(client); // This creates "Async" versions of methods that return promises
      return client;
    });
}

// A singleton client instance to be reused throughout the application
let clientInstance = null;

/**
 * Gets a Cassandra client instance.
 */
export function getCassandraClient() {
  if (clientInstance === null) {
    throw new Error('No client instance found. Did you forget to call initCassandraAsync?');
  }

  return clientInstance;
};

/**
 * Initializes Cassandra by creating any keyspace and tables necessary, and making sure the
 * client can connect to the cluster. Should be called before using the getCassandraClient
 * method in this module. Returns a Promise.
 */
export function initCassandraAsync() {
  logger.log('info', 'Initializing cassandra');

  // Create the client with some default query options
  return createClientAsync('killrvideo', {
      prepare: true,
      consistency: CassandraTypes.consistencies.localQuorum
    })
    .tap(client => {
      // Wait until Cassandra is ready and we can connect (could be delayed if starting up for 1st time)
      return withRetries(() => client.connectAsync(), 10, 10, 'Error connecting to cassandra', false);
    })
    .tap(client => {
      // Save client instance for reuse everywhere and log
      clientInstance = client;
      logger.log('info', 'Cassandra initialized')
    });
};
<commit_msg>Verify DB init<commit_after>import Promise from 'bluebird';
import { Client, types as CassandraTypes } from 'cassandra-driver';
import { lookupServiceAsync } from './service-discovery';
import { withRetries } from './with-retries';
import { logger } from './logging';

/**
 * Looks up the location of the cassandra service and creates a client with the options/keyspace
 * specified. Returns a Promise of the created client.
 */
function createClientAsync(keyspace, queryOptions) {
  return lookupServiceAsync('cassandra')
    .then(contactPoints => {
      let client = new Client({ contactPoints, keyspace, queryOptions });
      Promise.promisifyAll(client); // This creates "Async" versions of methods that return promises
      return client;
    });
}

// A singleton client instance to be reused throughout the application
let clientInstance = null;

/**
 * Gets a Cassandra client instance.
 */
export function getCassandraClient() {
  if (clientInstance === null) {
    throw new Error('No client instance found. Did you forget to call initCassandraAsync?');
  }

  return clientInstance;
};

/**
 * Initializes Cassandra by creating any keyspace and tables necessary, and making sure the
 * client can connect to the cluster. Should be called before using the getCassandraClient
 * method in this module. Returns a Promise.
 */
export function initCassandraAsync() {
  logger.log('info', 'Initializing cassandra...');

  // Create the client with some default query options
  return createClientAsync('killrvideo', {
      prepare: true,
      consistency: CassandraTypes.consistencies.localQuorum
    })
    .tap(client => {
      // Wait until Cassandra is ready and we can connect (could be delayed if starting up for 1st time)
      return withRetries(() => client.connectAsync(), 10, 10, 'Connecting to cassandra...', false, true);
    })
    .tap(client => {
      // Wait until Cassandra is bootstrapped and we can use it (dse-config needs time to initialise it)
      return withRetries(() =>
        new Promise (
          function(resolve, reject){
            client.execute(
              'SELECT keyspace_name FROM system_schema.keyspaces WHERE keyspace_name=\'kv_init_done\';', [], [],
              function(err, result) {
                if (err || result.rowLength != 1) {
                  reject(new Error('DB is not initialised'));
                }
                resolve();
              }
            )
          }
        ), 10, 10, 'Waiting for dse-config to bootstrap cassandra...', false, false
      );
    })
    .tap(client => {
      // Save client instance for reuse everywhere and log
      clientInstance = client;
      logger.log('info', 'Cassandra initialized')
    });
};
<|endoftext|>