<commit_before>/*jslint node:true */
module.exports = function (config) {
  'use strict';
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // web server port
    port: 9876,
    proxies:  {'/': 'http://localhost:8000/'},

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Set timeouts to avoid DISCONNECTED messages
    captureTimeout: 180000,  // default 60000
    browserDisconnectTimeout: 36000, // default 2000
    browserDisconnectTolerance: 1, // default 0
    browserNoActivityTimeout: 180000, //default 10000

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'story', 'html', 'junit', 'coverage', 'saucelabs', 'unicorn'],

    // Coverage report options
    coverageReporter: {
      reporters: [ {
        type: 'lcovonly',
        dir: 'build/coverage/',
        file: 'lcov.info'
      }, {
        type: 'cobertura',
        dir: 'shippable/codecoverage',
        file: 'test-coverage.xml'
      } ]
    },

    // JUnit report options (for Shippable)
    junitReporter: { outputFile: 'shippable/testresults/test-results.xml', suite: '' },

    // SauceLabs config that gets overwritten in Gruntfile.js
    sauceLabs: {},
    customLaunchers: {}
  });
};
<commit_msg>Upping timeouts again<commit_after>/*jslint node:true */
module.exports = function (config) {
  'use strict';
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // web server port
    port: 9876,
    proxies:  {'/': 'http://localhost:8000/'},

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Set timeouts to avoid DISCONNECTED messages
    captureTimeout: 180000,  // default 60000
    browserDisconnectTimeout: 60000, // default 2000
    browserDisconnectTolerance: 1, // default 0
    browserNoActivityTimeout: 300000, //default 10000

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'story', 'html', 'junit', 'coverage', 'saucelabs', 'unicorn'],

    // Coverage report options
    coverageReporter: {
      reporters: [ {
        type: 'lcovonly',
        dir: 'build/coverage/',
        file: 'lcov.info'
      }, {
        type: 'cobertura',
        dir: 'shippable/codecoverage',
        file: 'test-coverage.xml'
      } ]
    },

    // JUnit report options (for Shippable)
    junitReporter: { outputFile: 'shippable/testresults/test-results.xml', suite: '' },

    // SauceLabs config that gets overwritten in Gruntfile.js
    sauceLabs: {},
    customLaunchers: {}
  });
};
<|endoftext|>


<commit_before>/*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

/*
these are the configuration settings for TinyMCE. a complete list
of options can be found in the excellent manual at
http://tinymce.moxiecode.com/tinymce/docs/reference_configuration.html
*/

    currentURL = location.href.replace(/\?.*$/,"");
    //alert(currentURL);
    config = {

    /* enable customizable theme */
    theme    : "advanced",

    /* only the element whose ID is listed under "elements" will be editable */
    mode     : "exact",
    elements : "tinymce.content",

    /* FIXME: how do I get the user's language preference for authoring? */
    language : "en",

    /* enable plugins for custom save function, fullscreen editing
      and search-and-replace. */
    plugins  : "save,fullscreen,searchreplace,table,contextmenu,simplebrowser",

       plugin_simplebrowser_browselinkurl : currentURL + '?lenya.module=tinymce&lenya.step=link-show&language=en',
       plugin_simplebrowser_browseimageurl : currentURL + '?lenya.usecase=tinymce.insertAsset',
       plugin_simplebrowser_browseflashurl : '',

    /* grey out the "save" button unless there are unsaved changes: */
    save_enablewhendirty : false,

    /* some special settings for fullscreen mode (they override the
      settings further down) */
    fullscreen_settings : {
            auto_resize : false,
            theme_advanced_toolbar_location : "top",
            theme_advanced_statusbar_location : "bottom"
    },

    /* auto-resize the editing area. docs say this is "experimental"! */
    auto_resize : false,

    /* characters that should be replaced by named XHTML entities.
       cocoon does not define entities by default, so we use none. */
    entities        : "",

    /* do not make unicode references, output all special characters unchanged. */
    entity_encoding : "raw",

    /* keep linebreaks, don't put everything on one line. this is
       important for advanced users who want to use the source editor. */
    remove_linebreaks       : false,

    /* prettyprint sourcecode on saving FIXME: test. does this do anything? */
    apply_source_formatting : true,

    /* if the user inserts a table into a p, split p to create correct code.
       for nested lists, the inner list is correctly placed in a

    fix_list_elements: true;
    fix_table_elements: true;

    /* let tinymce do the layout by itself */
    theme_advanced_layout_manager     : "SimpleLayout",

    /* the toolbar is placed at the top of the editable area. */
    theme_advanced_toolbar_location   : "top",

    /* the statusbar is placed at the bottom. it displays the element path. */
    theme_advanced_statusbar_location : "bottom",

    /* the following items define the function buttons your users get to see.
      for a complete list, see
      http://tinymce.moxiecode.com/tinymce/docs/reference_buttons.html */

    theme_advanced_buttons1 :
            "save,undo,redo,search,replace,separator," +
            "cleanup,code,charmap,visualaid,fullscreen,separator," +
            "formatselect,styleselect",

    theme_advanced_buttons2 :
            "bold,italic,underline,strikethrough,sub,sup,removeformat,separator," +
            "bullist,numlist,table,separator," +
            "indent,outdent,separator," +
            "anchor,link,unlink,image,separator," +
            "help",

    /* three button rows are active by default. override. */
    theme_advanced_buttons3 : "",

    /* the following items define the allowed elements and attributes on your site.
      the default setting is very restrictive to enforce a corporate design and clean
      code at the expense of flexibility. */
    theme_advanced_blockformats       : "p,h1,h2,h3,h4",

/*
Lenya/TinyMCE glue code
*/

    /* this callback can override arbitrary tinymce commands. neato!
       currently used hooks are "insert image", "insert link" and "save".
       the handler sits in tiny_lenya_glue.js. */
    execcommand_callback : "LenyaExecCommandHandler",

    /* insert custom save handler to do some extra clean-up. */
    save_callback : "LenyaSaveContent"
    };

<commit_msg>Changed to insertImage usecase. Now has width and height fields and is consistent with BXE and FCK.<commit_after>/*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

/*
these are the configuration settings for TinyMCE. a complete list
of options can be found in the excellent manual at
http://tinymce.moxiecode.com/tinymce/docs/reference_configuration.html
*/

    currentURL = location.href.replace(/\?.*$/,"");
    //alert(currentURL);
    config = {

    /* enable customizable theme */
    theme    : "advanced",

    /* only the element whose ID is listed under "elements" will be editable */
    mode     : "exact",
    elements : "tinymce.content",

    /* FIXME: how do I get the user's language preference for authoring? */
    language : "en",

    /* enable plugins for custom save function, fullscreen editing
      and search-and-replace. */
    plugins  : "save,fullscreen,searchreplace,table,contextmenu,simplebrowser",

       plugin_simplebrowser_browselinkurl : currentURL + '?lenya.module=tinymce&lenya.step=link-show&language=en',
       plugin_simplebrowser_browseimageurl : currentURL + '?lenya.usecase=tinymce.insertImage',
       plugin_simplebrowser_browseflashurl : '',

    /* grey out the "save" button unless there are unsaved changes: */
    save_enablewhendirty : false,

    /* some special settings for fullscreen mode (they override the
      settings further down) */
    fullscreen_settings : {
            auto_resize : false,
            theme_advanced_toolbar_location : "top",
            theme_advanced_statusbar_location : "bottom"
    },

    /* auto-resize the editing area. docs say this is "experimental"! */
    auto_resize : false,

    /* characters that should be replaced by named XHTML entities.
       cocoon does not define entities by default, so we use none. */
    entities        : "",

    /* do not make unicode references, output all special characters unchanged. */
    entity_encoding : "raw",

    /* keep linebreaks, don't put everything on one line. this is
       important for advanced users who want to use the source editor. */
    remove_linebreaks       : false,

    /* prettyprint sourcecode on saving FIXME: test. does this do anything? */
    apply_source_formatting : true,

    /* if the user inserts a table into a p, split p to create correct code.
       for nested lists, the inner list is correctly placed in a

    fix_list_elements: true;
    fix_table_elements: true;

    /* let tinymce do the layout by itself */
    theme_advanced_layout_manager     : "SimpleLayout",

    /* the toolbar is placed at the top of the editable area. */
    theme_advanced_toolbar_location   : "top",

    /* the statusbar is placed at the bottom. it displays the element path. */
    theme_advanced_statusbar_location : "bottom",

    /* the following items define the function buttons your users get to see.
      for a complete list, see
      http://tinymce.moxiecode.com/tinymce/docs/reference_buttons.html */

    theme_advanced_buttons1 :
            "save,undo,redo,search,replace,separator," +
            "cleanup,code,charmap,visualaid,fullscreen,separator," +
            "formatselect,styleselect",

    theme_advanced_buttons2 :
            "bold,italic,underline,strikethrough,sub,sup,removeformat,separator," +
            "bullist,numlist,table,separator," +
            "indent,outdent,separator," +
            "anchor,link,unlink,image,separator," +
            "help",

    /* three button rows are active by default. override. */
    theme_advanced_buttons3 : "",

    /* the following items define the allowed elements and attributes on your site.
      the default setting is very restrictive to enforce a corporate design and clean
      code at the expense of flexibility. */
    theme_advanced_blockformats       : "p,h1,h2,h3,h4",

/*
Lenya/TinyMCE glue code
*/

    /* this callback can override arbitrary tinymce commands. neato!
       currently used hooks are "insert image", "insert link" and "save".
       the handler sits in tiny_lenya_glue.js. */
    execcommand_callback : "LenyaExecCommandHandler",

    /* insert custom save handler to do some extra clean-up. */
    save_callback : "LenyaSaveContent"
    };

<|endoftext|>


<commit_before>/*
 * moleculer
 * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer)
 * MIT Licensed
 */

"use strict";

const _ = require("lodash");

function wrapLocalTracingMiddleware(handler, action) {
	let opts = action.tracing;
	if (opts === true || opts === false)
		opts = { enabled: !!opts };
	opts = _.defaultsDeep({}, opts, { enabled: true });

	if (this.isTracingEnabled() && opts.enabled) {
		return function tracingMiddleware(ctx) {

			const tags = {
				callingLevel: ctx.level,
				action: ctx.action ? {
					name: ctx.action.name
				} : null,
				remoteCall: ctx.nodeID !== ctx.broker.nodeID,
				callerNodeID: ctx.nodeID,
				options: {
					timeout: ctx.options.timeout,
					retries: ctx.options.retries
				}
			};

			if (_.isFunction(opts.tags)) {
				const res = opts.tags.call(ctx.service, ctx);
				if (res)
					Object.assign(tags, res);
			} else if (Array.isArray(opts.tags)) {
				opts.tags.forEach(key => {
					if (key.startsWith("#"))
						tags[key.slice(1)] = _.get(ctx.meta, key.slice(1));
					else
						tags[key] = _.get(ctx.params, key);
				});
			}

			const span = ctx.broker.tracer.startSpan(`call '${ctx.action.name}'`, {
				id: ctx.id,
				traceID: ctx.requestID,
				parentID: ctx.parentID,
				service: ctx.service ? {
					name: ctx.service.name,
					version: ctx.service.version,
					fullName: ctx.service.fullName,
				} : null,
				sampled: ctx.tracing,
				tags
			}, ctx);

			ctx.tracing = span.sampled;
			ctx.span = span;

			// Call the handler
			return handler(ctx).then(res => {
				span.addTags({
					fromCache: ctx.cachedResult
				}).finish();

				//ctx.duration = span.duration;

				return res;
			}).catch(err => {
				span.setError(err).finish();

				return this.Promise.reject(err);
			});

		}.bind(this);
	}

	return handler;
}

/*
function wrapRemoteTracingMiddleware(handler) {

	if (this.options.tracing) {
		return function tracingMiddleware(ctx) {
			if (ctx.tracing == null) {
				ctx.tracing = shouldTracing(ctx);
			}
			return handler(ctx);

		}.bind(this);
	}

	return handler;
}*/

module.exports = function TracingMiddleware() {
	return {
		localAction: wrapLocalTracingMiddleware,
		//remoteAction: wrapRemoteTracingMiddleware
	};
};
<commit_msg>add action rawName<commit_after>/*
 * moleculer
 * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer)
 * MIT Licensed
 */

"use strict";

const _ = require("lodash");

function wrapLocalTracingMiddleware(handler, action) {
	let opts = action.tracing;
	if (opts === true || opts === false)
		opts = { enabled: !!opts };
	opts = _.defaultsDeep({}, opts, { enabled: true });

	if (this.isTracingEnabled() && opts.enabled) {
		return function tracingMiddleware(ctx) {

			const tags = {
				callingLevel: ctx.level,
				action: ctx.action ? {
					name: ctx.action.name,
					rawName: ctx.action.rawName
				} : null,
				remoteCall: ctx.nodeID !== ctx.broker.nodeID,
				callerNodeID: ctx.nodeID,
				options: {
					timeout: ctx.options.timeout,
					retries: ctx.options.retries
				}
			};

			if (_.isFunction(opts.tags)) {
				const res = opts.tags.call(ctx.service, ctx);
				if (res)
					Object.assign(tags, res);
			} else if (Array.isArray(opts.tags)) {
				opts.tags.forEach(key => {
					if (key.startsWith("#"))
						tags[key.slice(1)] = _.get(ctx.meta, key.slice(1));
					else
						tags[key] = _.get(ctx.params, key);
				});
			}

			const span = ctx.broker.tracer.startSpan(`action '${ctx.action.name}'`, {
				id: ctx.id,
				traceID: ctx.requestID,
				parentID: ctx.parentID,
				service: ctx.service ? {
					name: ctx.service.name,
					version: ctx.service.version,
					fullName: ctx.service.fullName,
				} : null,
				sampled: ctx.tracing,
				tags
			}, ctx);

			ctx.tracing = span.sampled;
			ctx.span = span;

			// Call the handler
			return handler(ctx).then(res => {
				span.addTags({
					fromCache: ctx.cachedResult
				}).finish();

				//ctx.duration = span.duration;

				return res;
			}).catch(err => {
				span.setError(err).finish();

				return this.Promise.reject(err);
			});

		}.bind(this);
	}

	return handler;
}

/*
function wrapRemoteTracingMiddleware(handler) {

	if (this.options.tracing) {
		return function tracingMiddleware(ctx) {
			if (ctx.tracing == null) {
				ctx.tracing = shouldTracing(ctx);
			}
			return handler(ctx);

		}.bind(this);
	}

	return handler;
}*/

module.exports = function TracingMiddleware() {
	return {
		localAction: wrapLocalTracingMiddleware,
		//remoteAction: wrapRemoteTracingMiddleware
	};
};
<|endoftext|>


<commit_before>'use strict';

// What is Controller?
// ---

// Controller helps facilitate routing via express
// by providing configuring route handlers
//
// For example, a route to `/users/:id`
// would be handled by a `UsersController` with function `findOne`
//
// It provides a way for each controller
// to setup the routes and handlers it wants to respond to
//
// Also provides a mechanism to define pre, before, and after middleware
// per controller or per route
//
// Finally, it also provides response and error handling middleware
//
// Also parses query strings for filter, limit, and sort

// Dependencies
// ---
var _ = require('lodash');
var Backbone = require('backbone');
var Model = require('./model');
var Collection = require('./collection');
var xml2js = require('xml2js');

module.exports = Backbone.Model.extend({
  debug: false,

  path: '/',

  sortParam: 'created',
  sortOrder: 'desc',
  skip: 0,
  limit: 100,

  xmlBuilder: new xml2js.Builder(),

  // Route specific middleware definitions
  // Object or Function
  middleware: function() {
    return {};
  },

  // Database query parameters/filters
  // Object or Function
  queryParams: function() {
    return {};
  },

  // Computes the base path for the controller
  // Object or Function
  basePath: function() {
    return this.path;
  },

  // Called after the constructor
  initialize: function() {
    // Routes
    this.routes = {
      all: {},
      get: {},
      post: {},
      put: {},
      patch: {},
      delete: {}
    };

    // Middleware(s)
    this.pre = []; // run before route middleware
    this.before = []; // run after route middleware but before route handler
    this.after = []; // run after route handler

    // Setup middleware and route handlers
    this.setupPreMiddleware();
    this.setupBeforeMiddleware();
    this.setupRoutes();
    this.setupAfterMiddleware();

    // Response/error handler middleware
    this.after.push(this.successResponse);
    this.after.push(this.errorResponse);
    this.after.push(this.finalResponse);
  },

  // Setup routes that this controller should handle
  //
  // Example:
  // this.routes.get['/test'] = {
  //   action: this.testGet,
  //   middleware: []
  // };
  setupRoutes: function() {},

  // Setup middleware that should run before the route middleware
  // Example: `this.pre.push(this.fakePreMiddleware)`
  setupPreMiddleware: function() {},

  // Setup middleware that should run before the route handler
  // Example: `this.before.push(this.fakeBeforeMiddleware)`
  setupBeforeMiddleware: function() {},

  // Setup middleware that should run after the route handler
  // Example: `this.after.push(this.fakeAfterMiddleware)`
  setupAfterMiddleware: function() {},

  // Middleware
  // ---

  // Render a model or a collection in the response
  // Used as a promise resolver in a `.then` promise handler
  // Always bind inner function to `this` original context
  render: function(req, res, next) {
    return function(modelOrCollection) {
      this.prepareResponse(modelOrCollection, req, res, next);
    }.bind(this);
  },

  // Deprecated (see `render`)
  nextThen: function(req, res, next) {
    return this.render(req, res, next);
  },

  // This method can be overridden to customize the response
  prepareResponse: function(modelOrCollection, req, res, next) {
    if (!modelOrCollection) {
      return next();
    }

    if (modelOrCollection instanceof Model) {
      // Data is a Model
      res.data = this.renderModel(modelOrCollection);
    } else if (modelOrCollection instanceof Collection) {
      // Data is a Collection
      res.data = this.renderCollection(modelOrCollection);
    } else {
      // Data is raw
      res.data = modelOrCollection;
    }

    return next();
  },

  // Default middleware for handling successful responses
  successResponse: function(req, res, next) {
    var data = res.data || null;
    var code = 200;
    if (_.isNumber(res.code)) {
      code = res.code;
    }
    var envelope = {
      meta: {
        code: code
      },
      data: data
    };

    // Optional paging meta
    if (res.paging) {
      envelope.meta.paging = res.paging;
    }

    // Set code and data
    res.code = code;
    if (res.code !== 204) {
      res.data = envelope;
    }

    return next();
  },

  // Default middleware for handling error responses
  errorResponse: function(err, req, res, next) {
    var data = err.message || 'Internal Server Error';
    var code = 500;
    if (_.isNumber(err.code)) {
      code = err.code;
    } else if (_.isNumber(res.code)) {
      code = res.code;
    }
    var error = {
      message: data,
      code: code
    };
    if (_.isString(err.type)) {
      error.type = err.type;
    }
    var envelope = {
      meta: {
        code: code,
        error: error
      },
      data: data
    };

    // TODO
    // We should log these errors somewhere remotely
    if (this.debug) {
      if (code >= 500) {
        if (err && err.stack) {
          console.error('Request Error: %j', err.stack, {});
        }
      } else {
        console.error('Request Error (%d): %s', code, err.message);
      }
    }

    // Set code and data
    res.code = code;
    res.data = envelope;

    return next();
  },

  // Final middleware for handling all responses
  // Server actually responds to the request here
  finalResponse: function(req, res, next) {
    // If we timed out before managing to respond, don't send the response
    if (res.headersSent) {
      return;
    }

    // Look for `.json` or `.xml` extension in path
    // And override request accept header
    if (/.json$/.test(req.path)) {
      req.headers.accept = 'application/json';
    } else if (/.xml$/.test(req.path)) {
      req.headers.accept = 'application/xml';
    }

    // Use request accept header to determine response content-type
    res.format({
      json: function() {
        res.status(res.code).jsonp(res.data);
      },
      xml: function() {
        res.set('Content-Type', 'application/xml; charset=utf-8');

        var xml;
        try {
          var xmlData = JSON.parse(JSON.stringify(res.data));
          xml = this.xmlBuilder.buildObject(xmlData);
          res.status(res.code).send(xml);
        } catch (e) {
          console.error('XML building error: %s', e.stack);
          res.status(res.code);
        }
      }.bind(this)
    });
  },

  // Render
  // ---

  renderModel: function(model) {
    return model.render();
  },

  renderCollection: function(collection) {
    return collection.map(function(model) {
      return model.render();
    });
  },

  // Helpers
  // ---

  // For `created` and `updated` date range query string
  buildTimestampQuery: function(query) {
    var result = {};
    if (!_.isObject(query) || _.isEmpty(query)) {
      return result;
    }

    // timestamp might be in `ms` or `s`
    _.each(query, function(timestamp, operator) {
      if (!_.contains(['gt', 'gte', 'lt', 'lte', 'ne'], operator)) {
        return;
      }

      // Timestamp must be an integer
      timestamp = _.parseInt(timestamp);
      if (_.isNaN(timestamp)) {
        return;
      }

      // Convert seconds to milliseconds
      timestamp = _.isUnixTime(timestamp) ? timestamp * 1000 : timestamp;

      result['$' + operator] = timestamp;
    });

    return result;
  },

  // Parses req.query (querystring) for since/until, sort/order, skip/limit
  // Also builds a query using allowed queryParams if applicable
  parseQueryString: function(req, options) {
    var query = {};
    var queries = [];
    options = options || {};

    // Need to make sure these are strings, they get parsed to int later
    options.skip = options.skip ? options.skip.toString() : options.skip;
    options.limit = options.limit ? options.limit.toString() : options.limit;

    // Reserved Params
    var fields = {};
    var created = req.query.created || {}; // accepts both s and ms
    var updated = req.query.updated || {}; // accepts both s and ms
    var sortBy = options.sortParam || req.query.sort || this.sortParam;
    var orderBy = options.sortOrder || req.query.order || this.sortOrder;
    var skip = options.skip || req.query.skip || req.query.offset || this.skip;
    var limit = options.limit || req.query.limit || req.query.count || this.limit;
    skip = _.parseInt(skip) || 0;
    limit = _.parseInt(limit) || 0;
    limit = Math.min(limit, this.limit); // Hard limit at 100

    var page = _.parseInt(req.query.page);
    if (page > 0) {
      // IMPORTANT! `page` starts at 1
      // if `page` is specified, we override `skip`
      // calculate skip based on page and limit
      // lets assume limit is 100
      // page 1 is skip 0
      // page 2 is skip 100
      // etc...
      skip = (page - 1) * limit;
    }

    // Date Range params
    var createdQuery = this.buildTimestampQuery(created);
    var updatedQuery = this.buildTimestampQuery(updated);

    if (!_.isEmpty(createdQuery)) {
      queries.push({
        created: createdQuery
      });
    }
    if (!_.isEmpty(updatedQuery)) {
      queries.push({
        updated: updatedQuery
      });
    }

    // Fields
    if (_.isString(req.query.fields)) {
      _.each(req.query.fields.split(','), function(field) {
        fields[field] = 1;
      });
    }
    if (_.isObject(options.fields)) {
      _.extend(fields, options.fields);
    }

    // Filter params
    var queryParams = _.extend(_.result(this, 'queryParams'), {
      'user_id': 'string'
    });
    if (_.isObject(options.queryParams)) {
      _.extend(queryParams, options.queryParams);
    }
    var filterParams = _.pick(req.query, _.keys(queryParams));
    var logicalOperator = '$' + (req.query.logical || 'and').toLowerCase().replace(/[@\s]/g, '');

    _.each(filterParams, function(val, key) {
      // If value is all, ignore this param
      if (val === 'all') {
        return;
      }

      // Make sure val is a string (should usually be from express)
      if (!_.isString(val)) {
        val = val.toString();
      }

      // Support `,` as `$or` for each param
      var vals = val.split(',');

      // No value, ignore this param
      if (vals.length === 0) {
        return;
      }

      // The built query filter
      var filter = {};

      // Get param type
      var type = queryParams[key];

      // Deal with different param types
      if (type === 'string') {
        // strings and objectid
        // no transformation
      } else if (type === 'regex') {
        // regex case insensitive and escaping special characters
        vals = _.map(vals, function(v) {
          return {
            '$regex': _.escapeRegExp(v),
            '$options': 'i'
          };
        });
      } else if (type === 'integer') {
        // integers
        vals = _.map(vals, function(v) {
          return _.parseInt(v);
        });
      } else if (type === 'float') {
        // floats
        vals = _.map(vals, function(v) {
          return parseFloat(v);
        });
      } else {
        // invalid or unknown type
        return;
      }

      // If there is only one val, no need to use `$or`
      if (vals.length === 1) {
        filter[key] = vals[0];
      } else {
        var orExpr = [];
        _.each(vals, function(orVal) {
          var orClause = {};
          orClause[key] = orVal;
          orExpr.push(orClause);
        });
        filter['$or'] = orExpr;
      }

      queries.push(filter);
    });

    // Combine the query
    if (queries.length > 0) {
      query[logicalOperator] = queries;
    }

    // Options
    // Sort/Order/Limit/Skip
    var sortOptions = [
      [sortBy, orderBy]
    ];

    return {
      'query': query,
      'sort': sortOptions,
      'limit': limit,
      'skip': skip,
      'fields': fields
    };
  }
});
<commit_msg>force xml output to not be pretty<commit_after>'use strict';

// What is Controller?
// ---

// Controller helps facilitate routing via express
// by providing configuring route handlers
//
// For example, a route to `/users/:id`
// would be handled by a `UsersController` with function `findOne`
//
// It provides a way for each controller
// to setup the routes and handlers it wants to respond to
//
// Also provides a mechanism to define pre, before, and after middleware
// per controller or per route
//
// Finally, it also provides response and error handling middleware
//
// Also parses query strings for filter, limit, and sort

// Dependencies
// ---
var _ = require('lodash');
var Backbone = require('backbone');
var Model = require('./model');
var Collection = require('./collection');
var xml2js = require('xml2js');

module.exports = Backbone.Model.extend({
  debug: false,

  path: '/',

  sortParam: 'created',
  sortOrder: 'desc',
  skip: 0,
  limit: 100,

  xmlBuilder: new xml2js.Builder({
    renderOpts: {
      pretty: false
    }
  }),

  // Route specific middleware definitions
  // Object or Function
  middleware: function() {
    return {};
  },

  // Database query parameters/filters
  // Object or Function
  queryParams: function() {
    return {};
  },

  // Computes the base path for the controller
  // Object or Function
  basePath: function() {
    return this.path;
  },

  // Called after the constructor
  initialize: function() {
    // Routes
    this.routes = {
      all: {},
      get: {},
      post: {},
      put: {},
      patch: {},
      delete: {}
    };

    // Middleware(s)
    this.pre = []; // run before route middleware
    this.before = []; // run after route middleware but before route handler
    this.after = []; // run after route handler

    // Setup middleware and route handlers
    this.setupPreMiddleware();
    this.setupBeforeMiddleware();
    this.setupRoutes();
    this.setupAfterMiddleware();

    // Response/error handler middleware
    this.after.push(this.successResponse);
    this.after.push(this.errorResponse);
    this.after.push(this.finalResponse);
  },

  // Setup routes that this controller should handle
  //
  // Example:
  // this.routes.get['/test'] = {
  //   action: this.testGet,
  //   middleware: []
  // };
  setupRoutes: function() {},

  // Setup middleware that should run before the route middleware
  // Example: `this.pre.push(this.fakePreMiddleware)`
  setupPreMiddleware: function() {},

  // Setup middleware that should run before the route handler
  // Example: `this.before.push(this.fakeBeforeMiddleware)`
  setupBeforeMiddleware: function() {},

  // Setup middleware that should run after the route handler
  // Example: `this.after.push(this.fakeAfterMiddleware)`
  setupAfterMiddleware: function() {},

  // Middleware
  // ---

  // Render a model or a collection in the response
  // Used as a promise resolver in a `.then` promise handler
  // Always bind inner function to `this` original context
  render: function(req, res, next) {
    return function(modelOrCollection) {
      this.prepareResponse(modelOrCollection, req, res, next);
    }.bind(this);
  },

  // Deprecated (see `render`)
  nextThen: function(req, res, next) {
    return this.render(req, res, next);
  },

  // This method can be overridden to customize the response
  prepareResponse: function(modelOrCollection, req, res, next) {
    if (!modelOrCollection) {
      return next();
    }

    if (modelOrCollection instanceof Model) {
      // Data is a Model
      res.data = this.renderModel(modelOrCollection);
    } else if (modelOrCollection instanceof Collection) {
      // Data is a Collection
      res.data = this.renderCollection(modelOrCollection);
    } else {
      // Data is raw
      res.data = modelOrCollection;
    }

    return next();
  },

  // Default middleware for handling successful responses
  successResponse: function(req, res, next) {
    var data = res.data || null;
    var code = 200;
    if (_.isNumber(res.code)) {
      code = res.code;
    }
    var envelope = {
      meta: {
        code: code
      },
      data: data
    };

    // Optional paging meta
    if (res.paging) {
      envelope.meta.paging = res.paging;
    }

    // Set code and data
    res.code = code;
    if (res.code !== 204) {
      res.data = envelope;
    }

    return next();
  },

  // Default middleware for handling error responses
  errorResponse: function(err, req, res, next) {
    var data = err.message || 'Internal Server Error';
    var code = 500;
    if (_.isNumber(err.code)) {
      code = err.code;
    } else if (_.isNumber(res.code)) {
      code = res.code;
    }
    var error = {
      message: data,
      code: code
    };
    if (_.isString(err.type)) {
      error.type = err.type;
    }
    var envelope = {
      meta: {
        code: code,
        error: error
      },
      data: data
    };

    // TODO
    // We should log these errors somewhere remotely
    if (this.debug) {
      if (code >= 500) {
        if (err && err.stack) {
          console.error('Request Error: %j', err.stack, {});
        }
      } else {
        console.error('Request Error (%d): %s', code, err.message);
      }
    }

    // Set code and data
    res.code = code;
    res.data = envelope;

    return next();
  },

  // Final middleware for handling all responses
  // Server actually responds to the request here
  finalResponse: function(req, res, next) {
    // If we timed out before managing to respond, don't send the response
    if (res.headersSent) {
      return;
    }

    // Look for `.json` or `.xml` extension in path
    // And override request accept header
    if (/.json$/.test(req.path)) {
      req.headers.accept = 'application/json';
    } else if (/.xml$/.test(req.path)) {
      req.headers.accept = 'application/xml';
    }

    // Use request accept header to determine response content-type
    res.format({
      json: function() {
        res.status(res.code).jsonp(res.data);
      },
      xml: function() {
        res.set('Content-Type', 'application/xml; charset=utf-8');

        var xml;
        try {
          var xmlData = JSON.parse(JSON.stringify(res.data));
          xml = this.xmlBuilder.buildObject(xmlData);
          res.status(res.code).send(xml);
        } catch (e) {
          console.error('XML building error: %s', e.stack);
          res.status(res.code);
        }
      }.bind(this)
    });
  },

  // Render
  // ---

  renderModel: function(model) {
    return model.render();
  },

  renderCollection: function(collection) {
    return collection.map(function(model) {
      return model.render();
    });
  },

  // Helpers
  // ---

  // For `created` and `updated` date range query string
  buildTimestampQuery: function(query) {
    var result = {};
    if (!_.isObject(query) || _.isEmpty(query)) {
      return result;
    }

    // timestamp might be in `ms` or `s`
    _.each(query, function(timestamp, operator) {
      if (!_.contains(['gt', 'gte', 'lt', 'lte', 'ne'], operator)) {
        return;
      }

      // Timestamp must be an integer
      timestamp = _.parseInt(timestamp);
      if (_.isNaN(timestamp)) {
        return;
      }

      // Convert seconds to milliseconds
      timestamp = _.isUnixTime(timestamp) ? timestamp * 1000 : timestamp;

      result['$' + operator] = timestamp;
    });

    return result;
  },

  // Parses req.query (querystring) for since/until, sort/order, skip/limit
  // Also builds a query using allowed queryParams if applicable
  parseQueryString: function(req, options) {
    var query = {};
    var queries = [];
    options = options || {};

    // Need to make sure these are strings, they get parsed to int later
    options.skip = options.skip ? options.skip.toString() : options.skip;
    options.limit = options.limit ? options.limit.toString() : options.limit;

    // Reserved Params
    var fields = {};
    var created = req.query.created || {}; // accepts both s and ms
    var updated = req.query.updated || {}; // accepts both s and ms
    var sortBy = options.sortParam || req.query.sort || this.sortParam;
    var orderBy = options.sortOrder || req.query.order || this.sortOrder;
    var skip = options.skip || req.query.skip || req.query.offset || this.skip;
    var limit = options.limit || req.query.limit || req.query.count || this.limit;
    skip = _.parseInt(skip) || 0;
    limit = _.parseInt(limit) || 0;
    limit = Math.min(limit, this.limit); // Hard limit at 100

    var page = _.parseInt(req.query.page);
    if (page > 0) {
      // IMPORTANT! `page` starts at 1
      // if `page` is specified, we override `skip`
      // calculate skip based on page and limit
      // lets assume limit is 100
      // page 1 is skip 0
      // page 2 is skip 100
      // etc...
      skip = (page - 1) * limit;
    }

    // Date Range params
    var createdQuery = this.buildTimestampQuery(created);
    var updatedQuery = this.buildTimestampQuery(updated);

    if (!_.isEmpty(createdQuery)) {
      queries.push({
        created: createdQuery
      });
    }
    if (!_.isEmpty(updatedQuery)) {
      queries.push({
        updated: updatedQuery
      });
    }

    // Fields
    if (_.isString(req.query.fields)) {
      _.each(req.query.fields.split(','), function(field) {
        fields[field] = 1;
      });
    }
    if (_.isObject(options.fields)) {
      _.extend(fields, options.fields);
    }

    // Filter params
    var queryParams = _.extend(_.result(this, 'queryParams'), {
      'user_id': 'string'
    });
    if (_.isObject(options.queryParams)) {
      _.extend(queryParams, options.queryParams);
    }
    var filterParams = _.pick(req.query, _.keys(queryParams));
    var logicalOperator = '$' + (req.query.logical || 'and').toLowerCase().replace(/[@\s]/g, '');

    _.each(filterParams, function(val, key) {
      // If value is all, ignore this param
      if (val === 'all') {
        return;
      }

      // Make sure val is a string (should usually be from express)
      if (!_.isString(val)) {
        val = val.toString();
      }

      // Support `,` as `$or` for each param
      var vals = val.split(',');

      // No value, ignore this param
      if (vals.length === 0) {
        return;
      }

      // The built query filter
      var filter = {};

      // Get param type
      var type = queryParams[key];

      // Deal with different param types
      if (type === 'string') {
        // strings and objectid
        // no transformation
      } else if (type === 'regex') {
        // regex case insensitive and escaping special characters
        vals = _.map(vals, function(v) {
          return {
            '$regex': _.escapeRegExp(v),
            '$options': 'i'
          };
        });
      } else if (type === 'integer') {
        // integers
        vals = _.map(vals, function(v) {
          return _.parseInt(v);
        });
      } else if (type === 'float') {
        // floats
        vals = _.map(vals, function(v) {
          return parseFloat(v);
        });
      } else {
        // invalid or unknown type
        return;
      }

      // If there is only one val, no need to use `$or`
      if (vals.length === 1) {
        filter[key] = vals[0];
      } else {
        var orExpr = [];
        _.each(vals, function(orVal) {
          var orClause = {};
          orClause[key] = orVal;
          orExpr.push(orClause);
        });
        filter['$or'] = orExpr;
      }

      queries.push(filter);
    });

    // Combine the query
    if (queries.length > 0) {
      query[logicalOperator] = queries;
    }

    // Options
    // Sort/Order/Limit/Skip
    var sortOptions = [
      [sortBy, orderBy]
    ];

    return {
      'query': query,
      'sort': sortOptions,
      'limit': limit,
      'skip': skip,
      'fields': fields
    };
  }
});
<|endoftext|>


<commit_before>'use babel';

import RsyncSaveView from './rsync-save-view';
import { CompositeDisposable,File,BufferedNodeProcess } from 'atom';

export default {

    rsyncSaveView: null,
    modalPanel: null,
    subscriptions: null,
    debug: true,
    daemonProcessAll:{},
    projectRsyncData:{},

    config: {
        enableDebugFlag: {
            title: 'DebugFlag',
            type: 'boolean',
            default: true,
        }
    },

    activate(state) {
        this.rsyncSaveView = new RsyncSaveView(state.rsyncSaveViewState);
        this.modalPanel = atom.workspace.addModalPanel({
            item: this.rsyncSaveView.getElement(),
            visible: false
        });

        var flag = atom.config.get('rsync-save.enableDebugFlag');
        if (false == flag) {
            this.debug = false;
        } else {
            this.debug = true;
        }

        var instance = this;
        atom.config.onDidChange('rsync-save.enableDebugFlag', 'rsync-save', function(event) {
            if (false == event.newValue) {
                instance.debug = false;
            } else {
                instance.debug = true;
            }
        });

        // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
        this.subscriptions = new CompositeDisposable();

        // Register command that toggles this view
        this.subscriptions.add(atom.commands.add('atom-workspace', {
            'rsync-save:toggle': () => this.toggle(),
        }));

        const projectPaths = atom.project.getPaths();
        projectPaths.forEach((projectPath, index) => {
            instance.checkProject(projectPath);
        });

        atom.project.onDidChangePaths(function(xProjectPaths) {
            var oldProjectPaths = {}
            for (var projectPath in instance.projectRsyncData) {
                oldProjectPaths[projectPath] = true;
            }
            for (var projectPath of xProjectPaths) {
                if (projectPath in oldProjectPaths) {
                    delete oldProjectPaths[projectPath];
                }
                instance.checkProject(projectPath);
            }
            for (var projectPath in oldProjectPaths) {
                if (projectPath in instance.projectRsyncData) {
                    instance.logInfo(`${projectPath} Remove`);
                    var oldRsyncData = instance.projectRsyncData[projectPath];
                    for (var delRsync in oldRsyncData) {
                        instance.killRsyncSave(delRsync);
                    }
                    delete instance.projectRsyncData[projectPath];
                }
            }
        });

        atom.workspace.observeTextEditors (function(editor) {
            editor.onDidSave(function() {
                var filePath = editor.getPath();
                if (editor.getTitle() == ".rsync_save.json") {
                    var end = filePath.lastIndexOf("/")
                    if (end > 0) {
                        var projectPath = filePath.substring(0, end);
                        instance.checkProject(projectPath);
                    }
                }
                console.log(filePath + " onDidSave");
            });
        });
    },

    deactivate() {
        var uKeyList = [];
        for (var uKey in this.daemonProcessAll) {
            uKeyList.push(uKey);
        }
        for(var uKey of uKeyList) {
            this.killRsyncSave(uKey);
        }

        this.modalPanel.destroy();
        this.subscriptions.dispose();
        this.rsyncSaveView.destroy();
    },

    serialize() {
        return {
            rsyncSaveViewState: this.rsyncSaveView.serialize()
        };
    },

    toggle() {
        console.log('RsyncSave was toggled!');
        var rsyncList = []
        for(var uKey in this.daemonProcessAll) {
            rsyncList.push(uKey);
        }
        this.rsyncSaveView.setRsyncList(rsyncList);

        return (
            this.modalPanel.isVisible() ?
            this.modalPanel.hide() :
            this.modalPanel.show()
        );
    },

    checkProject(xProjectPath) {
        var projectPath = xProjectPath;
        if (xProjectPath.endsWith("/")) {
            projectPath = xProjectPath.substring(0, xProjectPath.length - 1);
        }

        var confPath = projectPath + "/.rsync_save.json";
        var file = new File(confPath);
        if (!file.existsSync()) {
            this.logInfo(`${projectPath} IGNORED`);
            return;
        } else {
            this.logInfo(`${projectPath} RECHECK`);
        }

        var instance = this;
        file.read(false).then((text) => {
            console.log(`confPath ${confPath} text ${text}`);
            try {
                var retParse = JSON.parse(text);
                if (instance.isObj(retParse)) {
                    var confObjList = [retParse];
                } else if(instance.isArr(retParse)) {
                    var confObjList = retParse;
                } else {
                    var msg = `illegal conf ${confPath}`;
                    instance.logError(msg);
                    return;
                }
                if (!(projectPath in instance.projectRsyncData)) {
                    instance.projectRsyncData[projectPath] = {}
                }
                var oldRsyncData = instance.projectRsyncData[projectPath];

                var newRsyncData = {}
                var newRsyncList = []
                for (var confObj of confObjList) {
                    var checkedConf = instance.checkConfObj(projectPath, confObj);
                    if (!instance.isArr(checkedConf)) {
                        return;
                    } else if (checkedConf.length < 1) {
                        continue;
                    }

                    var uKey = instance.uKeyGen(checkedConf);
                    if (!(uKey in oldRsyncData)) {
                        instance.execRsyncSave(checkedConf, uKey);
                    }
                    newRsyncList.push(uKey);
                }

                for (var newRsync of newRsyncList) {
                    newRsyncData[newRsync] = true;
                    if (newRsync in oldRsyncData) {
                        this.logInfo(`${newRsync} WORKING`);
                        delete oldRsyncData[newRsync];
                    }
                }

                instance.projectRsyncData[projectPath] = newRsyncData;
                for (var delRsync in oldRsyncData) {
                    instance.killRsyncSave(delRsync);
                }
            } catch(e) {
                instance.logError(`with ${confPath} error:` + e);
            }
        });
    },

    checkConfObj(projectPath, confObj) {
        if (!this.isObj(confObj)) {
            var msg = `illegal conf ${confPath}`;
            this.logError(msg);
            return false;
        }

        var lPath = '';
        if (confObj["localPath"]) {
            var lPath = confObj["localPath"];
        }
        if (!lPath.startsWith("/")) {
            lPath = projectPath + lPath;
        }
        if (lPath.endsWith("/")) {
            lPath = lPath.substring(0, lPath.length - 1);
        }

        if (!confObj["enable"]) {
            var msg = `ignore ${lPath}`;
            this.logInfo(msg);
            return [];
        }
        if (!confObj["remoteHost"] || !confObj["remotePath"]) {
            var msg = `missing field remoteHost or remotePath`;
            this.logError(msg);
            return [];
        }

        var remote = confObj["remoteHost"];
        var rPath = confObj["remotePath"];
        checkedConf = [lPath, remote, rPath];
        return checkedConf;
    },

    createOnErrorFunc() {
        return (nodeError) => {
            this.logError('DaemonProcess error: ' + nodeError);
        };
    },

    killRsyncSave(uKey) {
        if (uKey in this.daemonProcessAll) {
            var daemonProcess = this.daemonProcessAll[uKey];
            this.logInfo(`${uKey} STOPPING`);
            daemonProcess.kill();
            delete this.daemonProcessAll[uKey];
        }
    },

    execRsyncSave(checkedConf, uKey) {
        const stdout = (output) => {
            if (this.debug) {
                console.log(output);
            }
        }

        const stderr = (output) => {
            console.error(output);
        }

        const exit = (code) => {
            var msg = "DaemonProcess exit with code " + code;
            if (0 != code) {
                this.logError(msg);
            } else if (this.debug) {
                this.logSuccess(msg);
            }
        }

        var command = __dirname + "/RsyncSaveDaemon.js";
        var args = checkedConf;
        var cwd = checkedConf[0];
        var options = {cwd};

        if (uKey in this.daemonProcessAll) {
            this.logInfo(`${uKey} REPEATED`);
            return;
        } else {
            this.logInfo(`${uKey} STARTING`);
        }

        var bnpArgs = {command, args, options, stdout, exit, stderr};
        var daemonProcess = new BufferedNodeProcess(bnpArgs);
        daemonProcess.onWillThrowError(this.createOnErrorFunc());
        this.daemonProcessAll[uKey] = daemonProcess
    },

    uKeyGen (checkedConf) {
        var uKey = checkedConf.join('#')
        return uKey;
    },

    logInfo (msg) {
        var xMsg = "rsync-save: " + msg;
        if (this.debug) {
            atom.notifications.addInfo(xMsg);
            console.log(xMsg);
        } else {
            console.log(xMsg);
        }
    },

    logError (msg) {
        var xMsg = "rsync-save: " + msg;
        atom.notifications.addError(xMsg);
        console.error(xMsg);
    },

    logSuccess(msg) {
        var xMsg = "rsync-save: " + msg;
        atom.notifications.addSuccess(xMsg);
        console.log(xMsg);
    },

    isObj (value) {
        return value && typeof value === 'object' && value.constructor === Object;
    },

    isArr (value) {
        return value && typeof value === 'object' && value.constructor === Array;
    }

};
<commit_msg>FIX: relative localPath process BUG<commit_after>'use babel';

import RsyncSaveView from './rsync-save-view';
import { CompositeDisposable,File,BufferedNodeProcess } from 'atom';

export default {

    rsyncSaveView: null,
    modalPanel: null,
    subscriptions: null,
    debug: true,
    daemonProcessAll:{},
    projectRsyncData:{},

    config: {
        enableDebugFlag: {
            title: 'DebugFlag',
            type: 'boolean',
            default: true,
        }
    },

    activate(state) {
        this.rsyncSaveView = new RsyncSaveView(state.rsyncSaveViewState);
        this.modalPanel = atom.workspace.addModalPanel({
            item: this.rsyncSaveView.getElement(),
            visible: false
        });

        var flag = atom.config.get('rsync-save.enableDebugFlag');
        if (false == flag) {
            this.debug = false;
        } else {
            this.debug = true;
        }

        var instance = this;
        atom.config.onDidChange('rsync-save.enableDebugFlag', 'rsync-save', function(event) {
            if (false == event.newValue) {
                instance.debug = false;
            } else {
                instance.debug = true;
            }
        });

        // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
        this.subscriptions = new CompositeDisposable();

        // Register command that toggles this view
        this.subscriptions.add(atom.commands.add('atom-workspace', {
            'rsync-save:toggle': () => this.toggle(),
        }));

        const projectPaths = atom.project.getPaths();
        projectPaths.forEach((projectPath, index) => {
            instance.checkProject(projectPath);
        });

        atom.project.onDidChangePaths(function(xProjectPaths) {
            var oldProjectPaths = {}
            for (var projectPath in instance.projectRsyncData) {
                oldProjectPaths[projectPath] = true;
            }
            for (var projectPath of xProjectPaths) {
                if (projectPath in oldProjectPaths) {
                    delete oldProjectPaths[projectPath];
                }
                instance.checkProject(projectPath);
            }
            for (var projectPath in oldProjectPaths) {
                if (projectPath in instance.projectRsyncData) {
                    instance.logInfo(`${projectPath} Remove`);
                    var oldRsyncData = instance.projectRsyncData[projectPath];
                    for (var delRsync in oldRsyncData) {
                        instance.killRsyncSave(delRsync);
                    }
                    delete instance.projectRsyncData[projectPath];
                }
            }
        });

        atom.workspace.observeTextEditors (function(editor) {
            editor.onDidSave(function() {
                var filePath = editor.getPath();
                if (editor.getTitle() == ".rsync_save.json") {
                    var end = filePath.lastIndexOf("/")
                    if (end > 0) {
                        var projectPath = filePath.substring(0, end);
                        instance.checkProject(projectPath);
                    }
                }
                console.log(filePath + " onDidSave");
            });
        });
    },

    deactivate() {
        var uKeyList = [];
        for (var uKey in this.daemonProcessAll) {
            uKeyList.push(uKey);
        }
        for(var uKey of uKeyList) {
            this.killRsyncSave(uKey);
        }

        this.modalPanel.destroy();
        this.subscriptions.dispose();
        this.rsyncSaveView.destroy();
    },

    serialize() {
        return {
            rsyncSaveViewState: this.rsyncSaveView.serialize()
        };
    },

    toggle() {
        console.log('RsyncSave was toggled!');
        var rsyncList = []
        for(var uKey in this.daemonProcessAll) {
            rsyncList.push(uKey);
        }
        this.rsyncSaveView.setRsyncList(rsyncList);

        return (
            this.modalPanel.isVisible() ?
            this.modalPanel.hide() :
            this.modalPanel.show()
        );
    },

    checkProject(xProjectPath) {
        var projectPath = xProjectPath;
        if (xProjectPath.endsWith("/")) {
            projectPath = xProjectPath.substring(0, xProjectPath.length - 1);
        }

        var confPath = projectPath + "/.rsync_save.json";
        var file = new File(confPath);
        if (!file.existsSync()) {
            this.logInfo(`${projectPath} IGNORED`);
            return;
        } else {
            this.logInfo(`${projectPath} RECHECK`);
        }

        var instance = this;
        file.read(false).then((text) => {
            console.log(`confPath ${confPath} text ${text}`);
            try {
                var retParse = JSON.parse(text);
                if (instance.isObj(retParse)) {
                    var confObjList = [retParse];
                } else if(instance.isArr(retParse)) {
                    var confObjList = retParse;
                } else {
                    var msg = `illegal conf ${confPath}`;
                    instance.logError(msg);
                    return;
                }
                if (!(projectPath in instance.projectRsyncData)) {
                    instance.projectRsyncData[projectPath] = {}
                }
                var oldRsyncData = instance.projectRsyncData[projectPath];

                var newRsyncData = {}
                var newRsyncList = []
                for (var confObj of confObjList) {
                    var checkedConf = instance.checkConfObj(projectPath, confObj);
                    if (!instance.isArr(checkedConf)) {
                        return;
                    } else if (checkedConf.length < 1) {
                        continue;
                    }

                    var uKey = instance.uKeyGen(checkedConf);
                    if (!(uKey in oldRsyncData)) {
                        instance.execRsyncSave(checkedConf, uKey);
                    }
                    newRsyncList.push(uKey);
                }

                for (var newRsync of newRsyncList) {
                    newRsyncData[newRsync] = true;
                    if (newRsync in oldRsyncData) {
                        this.logInfo(`${newRsync} WORKING`);
                        delete oldRsyncData[newRsync];
                    }
                }

                instance.projectRsyncData[projectPath] = newRsyncData;
                for (var delRsync in oldRsyncData) {
                    instance.killRsyncSave(delRsync);
                }
            } catch(e) {
                instance.logError(`with ${confPath} error:` + e);
            }
        });
    },

    checkConfObj(projectPath, confObj) {
        if (!this.isObj(confObj)) {
            var msg = `illegal conf ${confPath}`;
            this.logError(msg);
            return false;
        }

        var lPath = '';
        if (confObj["localPath"]) {
            var lPath = confObj["localPath"];
        }
        if (!lPath.startsWith("/")) {
            lPath = projectPath + '/' + lPath;
        }
        if (lPath.endsWith("/")) {
            lPath = lPath.substring(0, lPath.length - 1);
        }

        if (!confObj["enable"]) {
            var msg = `ignore ${lPath}`;
            this.logInfo(msg);
            return [];
        }
        if (!confObj["remoteHost"] || !confObj["remotePath"]) {
            var msg = `missing field remoteHost or remotePath`;
            this.logError(msg);
            return [];
        }

        var remote = confObj["remoteHost"];
        var rPath = confObj["remotePath"];
        checkedConf = [lPath, remote, rPath];
        return checkedConf;
    },

    createOnErrorFunc() {
        return (nodeError) => {
            this.logError('DaemonProcess error: ' + nodeError);
        };
    },

    killRsyncSave(uKey) {
        if (uKey in this.daemonProcessAll) {
            var daemonProcess = this.daemonProcessAll[uKey];
            this.logInfo(`${uKey} STOPPING`);
            daemonProcess.kill();
            delete this.daemonProcessAll[uKey];
        }
    },

    execRsyncSave(checkedConf, uKey) {
        const stdout = (output) => {
            if (this.debug) {
                console.log(output);
            }
        }

        const stderr = (output) => {
            console.error(output);
        }

        const exit = (code) => {
            var msg = "DaemonProcess exit with code " + code;
            if (0 != code) {
                this.logError(msg);
            } else if (this.debug) {
                this.logSuccess(msg);
            }
        }

        var command = __dirname + "/RsyncSaveDaemon.js";
        var args = checkedConf;
        var cwd = checkedConf[0];
        var options = {cwd};

        if (uKey in this.daemonProcessAll) {
            this.logInfo(`${uKey} REPEATED`);
            return;
        } else {
            this.logInfo(`${uKey} STARTING`);
        }

        var bnpArgs = {command, args, options, stdout, exit, stderr};
        var daemonProcess = new BufferedNodeProcess(bnpArgs);
        daemonProcess.onWillThrowError(this.createOnErrorFunc());
        this.daemonProcessAll[uKey] = daemonProcess
    },

    uKeyGen (checkedConf) {
        var uKey = checkedConf.join('#')
        return uKey;
    },

    logInfo (msg) {
        var xMsg = "rsync-save: " + msg;
        if (this.debug) {
            atom.notifications.addInfo(xMsg);
            console.log(xMsg);
        } else {
            console.log(xMsg);
        }
    },

    logError (msg) {
        var xMsg = "rsync-save: " + msg;
        atom.notifications.addError(xMsg);
        console.error(xMsg);
    },

    logSuccess(msg) {
        var xMsg = "rsync-save: " + msg;
        atom.notifications.addSuccess(xMsg);
        console.log(xMsg);
    },

    isObj (value) {
        return value && typeof value === 'object' && value.constructor === Object;
    },

    isArr (value) {
        return value && typeof value === 'object' && value.constructor === Array;
    }

};
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

.pragma library

var qmlProfilerModelProxy = 0;

//draw background of the graph
function drawGraph(canvas, ctxt)
{
    ctxt.fillStyle = "#eaeaea";
    ctxt.fillRect(0, 0, canvas.width, canvas.height);
}

//draw the actual data to be graphed
function drawData(canvas, ctxt)
{
    if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.isEmpty())
        return;

    var spacing = canvas.width / qmlProfilerModelProxy.traceDuration();

    for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
        for (var ii = canvas.offset; ii < qmlProfilerModelProxy.count(modelIndex);
             ii += canvas.increment) {

            var xx = (qmlProfilerModelProxy.startTime(modelIndex,ii) -
                      qmlProfilerModelProxy.traceStartTime()) * spacing;

            var eventWidth = qmlProfilerModelProxy.duration(modelIndex,ii) * spacing;

            if (eventWidth < 1)
                eventWidth = 1;

            xx = Math.round(xx);

            var itemHeight = qmlProfilerModelProxy.relativeHeight(modelIndex, ii) *
                    canvas.blockHeight;
            var yy = (modelIndex + 1) * canvas.blockHeight - itemHeight ;

            ctxt.fillStyle = qmlProfilerModelProxy.color(modelIndex, ii);
            ctxt.fillRect(xx, canvas.bump + yy, eventWidth, itemHeight);
        }
    }
}

function drawBindingLoops(canvas, ctxt) {
    ctxt.strokeStyle = "orange";
    ctxt.lineWidth = 2;
    var radius = 1;
    var spacing = canvas.width / qmlProfilerModelProxy.traceDuration();
    for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
        for (var ii = canvas.offset - canvas.increment; ii < qmlProfilerModelProxy.count(modelIndex);
             ii += canvas.increment) {
            if (qmlProfilerModelProxy.bindingLoopDest(modelIndex,ii) >= 0) {
                var xcenter = Math.round(qmlProfilerModelProxy.startTime(modelIndex,ii) +
                                         qmlProfilerModelProxy.duration(modelIndex,ii) -
                                         qmlProfilerModelProxy.traceStartTime()) * spacing;
                var ycenter = Math.round(canvas.bump + canvas.blockHeight * modelIndex +
                                         canvas.blockHeight / 2);
                ctxt.beginPath();
                ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
                ctxt.stroke();
            }
        }
    }
}

function drawNotes(canvas, ctxt)
{
    if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.noteCount() === 0)
        return;

    var spacing = canvas.width / qmlProfilerModelProxy.traceDuration();
    // divide canvas height in 7 parts: margin, 3*line, space, dot, margin
    var vertSpace = (canvas.height - canvas.bump) / 7;

    ctxt.strokeStyle = "orange";
    ctxt.lineWidth = 2;
    for (var i = 0; i < qmlProfilerModelProxy.noteCount(); ++i) {
        var timelineModel = qmlProfilerModelProxy.noteTimelineModel(i);
        var timelineIndex = qmlProfilerModelProxy.noteTimelineIndex(i);
        if (timelineIndex === -1)
            continue;
        var traceStart = qmlProfilerModelProxy.traceStartTime();
        var traceEnd = qmlProfilerModelProxy.traceEndTime();
        var start = Math.max(qmlProfilerModelProxy.startTime(timelineModel, timelineIndex),
                             traceStart);
        var end = Math.min(qmlProfilerModelProxy.endTime(timelineModel, timelineIndex),
                           traceEnd);
        var annoX = Math.round(((start + end) / 2 - traceStart) * spacing);

        ctxt.moveTo(annoX, canvas.bump + vertSpace)
        ctxt.lineTo(annoX, canvas.bump + vertSpace * 4)
        ctxt.stroke();
        ctxt.moveTo(annoX, canvas.bump + vertSpace * 5);
        ctxt.lineTo(annoX, canvas.bump + vertSpace * 6);
        ctxt.stroke();
    }
}

function drawTimeBar(canvas, ctxt)
{
    if (!qmlProfilerModelProxy)
        return;

    var width = canvas.width;
    var height = 10;
    var startTime = qmlProfilerModelProxy.traceStartTime();
    var endTime = qmlProfilerModelProxy.traceEndTime();

    var totalTime = qmlProfilerModelProxy.traceDuration();
    var spacing = width / totalTime;

    var initialBlockLength = 120;
    var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width *
                       initialBlockLength ) / Math.LN2 ) );
    var pixelsPerBlock = timePerBlock * spacing;
    var pixelsPerSection = pixelsPerBlock / 5;
    var blockCount = width / pixelsPerBlock;

    var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
    var realStartPos = (startTime-realStartTime) * spacing;

    var timePerPixel = timePerBlock/pixelsPerBlock;

    ctxt.fillStyle = "#000000";
    ctxt.font = "6px sans-serif";

    ctxt.fillStyle = "#cccccc";
    ctxt.fillRect(0, 0, width, height);
    for (var ii = 0; ii < blockCount+1; ii++) {
        var x = Math.floor(ii*pixelsPerBlock - realStartPos);

        // block boundary
        ctxt.strokeStyle = "#525252";
        ctxt.beginPath();
        ctxt.moveTo(x, height/2);
        ctxt.lineTo(x, height);
        ctxt.stroke();

        // block time label
        ctxt.fillStyle = "#000000";
        var timeString = prettyPrintTime((ii+0.5)*timePerBlock + realStartTime);
        ctxt.textAlign = "center";
        ctxt.fillText(timeString, x + pixelsPerBlock/2, height/2 + 3);
    }

    ctxt.fillStyle = "#808080";
    ctxt.fillRect(0, height-1, width, 1);
}

function prettyPrintTime( t )
{
    if (t <= 0) return "0";
    if (t<1000) return t+" ns";
    t = t/1000;
    if (t<1000) return t+" μs";
    t = Math.floor(t/100)/10;
    if (t<1000) return t+" ms";
    t = Math.floor(t)/1000;
    if (t<60) return t+" s";
    var m = Math.floor(t/60);
    t = Math.floor(t - m*60);
    return m+"m"+t+"s";
}
<commit_msg>QmlProfiler: Center the binding loop marker on events in overview<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

.pragma library

var qmlProfilerModelProxy = 0;

//draw background of the graph
function drawGraph(canvas, ctxt)
{
    ctxt.fillStyle = "#eaeaea";
    ctxt.fillRect(0, 0, canvas.width, canvas.height);
}

//draw the actual data to be graphed
function drawData(canvas, ctxt)
{
    if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.isEmpty())
        return;

    var spacing = canvas.width / qmlProfilerModelProxy.traceDuration();

    for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
        for (var ii = canvas.offset; ii < qmlProfilerModelProxy.count(modelIndex);
             ii += canvas.increment) {

            var xx = (qmlProfilerModelProxy.startTime(modelIndex,ii) -
                      qmlProfilerModelProxy.traceStartTime()) * spacing;

            var eventWidth = qmlProfilerModelProxy.duration(modelIndex,ii) * spacing;

            if (eventWidth < 1)
                eventWidth = 1;

            xx = Math.round(xx);

            var itemHeight = qmlProfilerModelProxy.relativeHeight(modelIndex, ii) *
                    canvas.blockHeight;
            var yy = (modelIndex + 1) * canvas.blockHeight - itemHeight ;

            ctxt.fillStyle = qmlProfilerModelProxy.color(modelIndex, ii);
            ctxt.fillRect(xx, canvas.bump + yy, eventWidth, itemHeight);
        }
    }
}

function drawBindingLoops(canvas, ctxt) {
    ctxt.strokeStyle = "orange";
    ctxt.lineWidth = 2;
    var radius = 1;
    var spacing = canvas.width / qmlProfilerModelProxy.traceDuration();
    for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); ++modelIndex) {
        for (var ii = canvas.offset - canvas.increment; ii < qmlProfilerModelProxy.count(modelIndex);
             ii += canvas.increment) {
            if (qmlProfilerModelProxy.bindingLoopDest(modelIndex,ii) >= 0) {
                var xcenter = Math.round(qmlProfilerModelProxy.startTime(modelIndex,ii) +
                                         qmlProfilerModelProxy.duration(modelIndex,ii) / 2 -
                                         qmlProfilerModelProxy.traceStartTime()) * spacing;
                var ycenter = Math.round(canvas.bump + canvas.blockHeight * modelIndex +
                                         canvas.blockHeight / 2);
                ctxt.beginPath();
                ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
                ctxt.stroke();
            }
        }
    }
}

function drawNotes(canvas, ctxt)
{
    if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.noteCount() === 0)
        return;

    var spacing = canvas.width / qmlProfilerModelProxy.traceDuration();
    // divide canvas height in 7 parts: margin, 3*line, space, dot, margin
    var vertSpace = (canvas.height - canvas.bump) / 7;

    ctxt.strokeStyle = "orange";
    ctxt.lineWidth = 2;
    for (var i = 0; i < qmlProfilerModelProxy.noteCount(); ++i) {
        var timelineModel = qmlProfilerModelProxy.noteTimelineModel(i);
        var timelineIndex = qmlProfilerModelProxy.noteTimelineIndex(i);
        if (timelineIndex === -1)
            continue;
        var traceStart = qmlProfilerModelProxy.traceStartTime();
        var traceEnd = qmlProfilerModelProxy.traceEndTime();
        var start = Math.max(qmlProfilerModelProxy.startTime(timelineModel, timelineIndex),
                             traceStart);
        var end = Math.min(qmlProfilerModelProxy.endTime(timelineModel, timelineIndex),
                           traceEnd);
        var annoX = Math.round(((start + end) / 2 - traceStart) * spacing);

        ctxt.moveTo(annoX, canvas.bump + vertSpace)
        ctxt.lineTo(annoX, canvas.bump + vertSpace * 4)
        ctxt.stroke();
        ctxt.moveTo(annoX, canvas.bump + vertSpace * 5);
        ctxt.lineTo(annoX, canvas.bump + vertSpace * 6);
        ctxt.stroke();
    }
}

function drawTimeBar(canvas, ctxt)
{
    if (!qmlProfilerModelProxy)
        return;

    var width = canvas.width;
    var height = 10;
    var startTime = qmlProfilerModelProxy.traceStartTime();
    var endTime = qmlProfilerModelProxy.traceEndTime();

    var totalTime = qmlProfilerModelProxy.traceDuration();
    var spacing = width / totalTime;

    var initialBlockLength = 120;
    var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width *
                       initialBlockLength ) / Math.LN2 ) );
    var pixelsPerBlock = timePerBlock * spacing;
    var pixelsPerSection = pixelsPerBlock / 5;
    var blockCount = width / pixelsPerBlock;

    var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
    var realStartPos = (startTime-realStartTime) * spacing;

    var timePerPixel = timePerBlock/pixelsPerBlock;

    ctxt.fillStyle = "#000000";
    ctxt.font = "6px sans-serif";

    ctxt.fillStyle = "#cccccc";
    ctxt.fillRect(0, 0, width, height);
    for (var ii = 0; ii < blockCount+1; ii++) {
        var x = Math.floor(ii*pixelsPerBlock - realStartPos);

        // block boundary
        ctxt.strokeStyle = "#525252";
        ctxt.beginPath();
        ctxt.moveTo(x, height/2);
        ctxt.lineTo(x, height);
        ctxt.stroke();

        // block time label
        ctxt.fillStyle = "#000000";
        var timeString = prettyPrintTime((ii+0.5)*timePerBlock + realStartTime);
        ctxt.textAlign = "center";
        ctxt.fillText(timeString, x + pixelsPerBlock/2, height/2 + 3);
    }

    ctxt.fillStyle = "#808080";
    ctxt.fillRect(0, height-1, width, 1);
}

function prettyPrintTime( t )
{
    if (t <= 0) return "0";
    if (t<1000) return t+" ns";
    t = t/1000;
    if (t<1000) return t+" μs";
    t = Math.floor(t/100)/10;
    if (t<1000) return t+" ms";
    t = Math.floor(t)/1000;
    if (t<60) return t+" s";
    var m = Math.floor(t/60);
    t = Math.floor(t - m*60);
    return m+"m"+t+"s";
}
<|endoftext|>