<commit_before>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * 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.
 */

/**
 * File Manager
 */
define('Mobile/SalesLogix/FileManager', [
    'dojo/_base/lang',
    'dojo/_base/declare',
    'dojo/number',
    'dojo/has',
    'dojo/_base/sniff'
], function(
    lang,
    declare,
    dNumber,
    has,
    sniff
) {
    return declare('Mobile.SalesLogix.FileManager', null, {
        unableToUploadText: 'This browser does not support HTML5 File API.',
        unknownSizeText: 'unknown',
        unknownErrorText: 'Warning: An error occured and the file failed to upload.',
        largeFileWarningText: 'Warning: This request exceeds the size limit set by your administrator and failed to upload.',
        largeFileWarningTitle: 'Warning',
        percentCompleteText: 'Uploading, please wait...',
        fileUploadOptions: { maxFileSize: 4000000 },
        _store: false,
        _totalProgress: 0,
        _files: null,
        _fileCount: 0,
        _filesUploadedCount: 0,
        _isUploading: false,

        constructor: function() {
            this._files = [];
            this.fileUploadOptions.maxFileSize = App.maxUploadFileSize;
        },
        isHTML5Supported:function(){
            var results = has('html5-file-api');
            return results;
        },
        isFileSizeAllowed: function(files) {
            var len = 0, maxfileSize, title, msg;
            maxfileSize = this.fileUploadOptions.maxFileSize;
            title = this.largeFileWarningTitle;
            msg = this.largeFileWarningText;

            for (var i = 0; i < files.length; i++) {
                if (files[i].size === 0) {
                    // do nothing.
                } else {
                    len += files[i].size || files[i].blob.length;
                }
            }

            if (len > (maxfileSize)) {
                return false;
            }

            return true;
        },
        uploadFile: function(file, url, progress, complete, error, scope, asPut) {
            this.uploadFileHTML5(file, url, progress, complete, error, scope, asPut);
        },
        uploadFileHTML5: function(file, url, progress, complete, error, scope, asPut) {
            if (!this.isFileSizeAllowed([file])) {
                this._onUnableToUploadError(this.largeFileWarningText, error);
                return;
            }
            if (this.isHTML5Supported()) {
                this._uploadFileHTML5_asBinary(file, url, progress, complete, error, scope, asPut);
            } else {
                this._onUnableToUploadError(this.unableToUploadText, error);
            }
        },
        _uploadFileHTML5_asBinary: function(file, url, progress, complete, error, scope, asPut) {
            window.BlobBuilder = window.BlobBuilder ||
                         window.WebKitBlobBuilder ||
                         window.MozBlobBuilder ||
                         window.MSBlobBuilder;
            if (!url) {
                //assume Attachment SData url
                url = 'slxdata.ashx/slx/system/-/attachments/file';// TODO: Remove this assumption from SDK
            }

            var request = new XMLHttpRequest(), service = App.getService(), reader;
            request.open((asPut) ? 'PUT' : 'POST', url);
            request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

            if (service) {
                request.setRequestHeader('Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization-Mode', 'no-challenge');
            }

            reader = new FileReader();
            reader.onload = lang.hitch(this, function(evt) {
                var binary, boundary, dashdash, crlf, bb, unknownErrorText, usingBlobBuilder, blobReader, blobData;
                unknownErrorText = this.unknownErrorText;
                blobReader = new FileReader();// read the blob as an ArrayBuffer to work around this android issue: https://code.google.com/p/android/issues/detail?id=39882

                try {
                    new Blob();
                    bb = [];
                } catch(e) {
                    bb = new window.BlobBuilder();
                    usingBlobBuilder = true;
                }

                binary = evt.target.result;
                boundary = "---------------------------" + (new Date()).getTime();
                dashdash = '--';
                crlf = '\r\n';

                this._append(bb, dashdash + boundary + crlf);
                this._append(bb, 'Content-Disposition: attachment; ');
                this._append(bb, 'name="file_"; ');
                this._append(bb, 'filename*="' + encodeURI(file.name) + '" ');
                this._append(bb, crlf);
                this._append(bb, 'Content-Type: ' + file.type);
                this._append(bb, crlf + crlf);
                this._append(bb, binary);
                this._append(bb, crlf);
                this._append(bb, dashdash + boundary + dashdash + crlf);

                if (complete) {
                    request.onreadystatechange = function() {
                        if (request.readyState === 4) {
                            if (Math.floor(request.status / 100) !== 2) {
                                if (error) {
                                    error.call(scope || this, unknownErrorText);
                                    console.warn(unknownErrorText + " "+ request.responseText);
                                }
                            } else {
                                complete.call(scope || this, request);
                            }
                        }
                    };
                }

                if (progress) {
                    request.upload.addEventListener('progress', function(e) {
                        progress.call(scope || this, e);
                    });
                }

                request.setRequestHeader('Content-Type', 'multipart/attachment; boundary=' + boundary);

                if (usingBlobBuilder) {
                    blobData = bb.getBlob(file.type);
                } else {
                    blobData = new Blob(bb);
                }

                // Read the blob back as an ArrayBuffer to work around this android issue:
                // https://code.google.com/p/android/issues/detail?id=39882
                blobReader.onload = function(e) {
                    request.send(e.target.result);
                }

                blobReader.readAsArrayBuffer(blobData);
            });

            reader.readAsArrayBuffer(file);
        },
        _append: function(arrayOrBlobBuilder, data) {
            if (arrayOrBlobBuilder && arrayOrBlobBuilder.constructor === Array) {
                arrayOrBlobBuilder.push(data);
            } else {
                arrayOrBlobBuilder.append(data);
            }
        },
        _onUnableToUploadError: function(msg, onError) {
            if (!msg) {
                msg = this.unableToUploadText
            }
            if (onError) {
                onError([msg])
            } else {
                console.warn([msg]);
            }
        },
        formatFileSize: function(size) {
            size = parseInt(size, 10);
            if (size === 0) {
                return '0 KB';
            }
            if (!size || size < 0) {
                return this.unknownSizeText;
            }
            if (size < 1024) {
                return '1 KB';
            }
            return dNumber.format(Math.round(size / 1024)) + ' KB';
        },
        getFile: function(fileUrl, responseType , onSuccess) {
            var request, service, self;

            request = new XMLHttpRequest();
            service = App.getService();
            self = this;

            request.open("GET", fileUrl, true);

            if (responseType) {
                request.responseType = responseType;
            }
            request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

            if (service) {
                request.setRequestHeader('Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization-Mode', 'no-challenge');
            }
            request.addEventListener("load", function() {
                var data, contentType, contentInfo, responseInfo, fileName;

                data = this.response;
                contentType = this.getResponseHeader("Content-Type");
                contentInfo = this.getResponseHeader("Content-Disposition");
                responseInfo = {};
                fileName = contentInfo.split('=')[1];

                responseInfo = {
                    request: this,
                    responseType: responseType,
                    response: this.response,
                    contentType: contentType,
                    fileName: fileName
                };
                if (onSuccess) {
                    onSuccess(responseInfo);
                }
            }, false);
            request.send(null);
        }
    });
});
<commit_msg>Added comment.<commit_after>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * 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.
 */

/**
 * File Manager
 */
define('Mobile/SalesLogix/FileManager', [
    'dojo/_base/lang',
    'dojo/_base/declare',
    'dojo/number',
    'dojo/has',
    'dojo/_base/sniff'
], function(
    lang,
    declare,
    dNumber,
    has,
    sniff
) {
    return declare('Mobile.SalesLogix.FileManager', null, {
        unableToUploadText: 'This browser does not support HTML5 File API.',
        unknownSizeText: 'unknown',
        unknownErrorText: 'Warning: An error occured and the file failed to upload.',
        largeFileWarningText: 'Warning: This request exceeds the size limit set by your administrator and failed to upload.',
        largeFileWarningTitle: 'Warning',
        percentCompleteText: 'Uploading, please wait...',
        fileUploadOptions: { maxFileSize: 4000000 },
        _store: false,
        _totalProgress: 0,
        _files: null,
        _fileCount: 0,
        _filesUploadedCount: 0,
        _isUploading: false,

        constructor: function() {
            this._files = [];
            this.fileUploadOptions.maxFileSize = App.maxUploadFileSize;
        },
        isHTML5Supported:function(){
            var results = has('html5-file-api');
            return results;
        },
        isFileSizeAllowed: function(files) {
            var len = 0, maxfileSize, title, msg;
            maxfileSize = this.fileUploadOptions.maxFileSize;
            title = this.largeFileWarningTitle;
            msg = this.largeFileWarningText;

            for (var i = 0; i < files.length; i++) {
                if (files[i].size === 0) {
                    // do nothing.
                } else {
                    len += files[i].size || files[i].blob.length;
                }
            }

            if (len > (maxfileSize)) {
                return false;
            }

            return true;
        },
        uploadFile: function(file, url, progress, complete, error, scope, asPut) {
            this.uploadFileHTML5(file, url, progress, complete, error, scope, asPut);
        },
        uploadFileHTML5: function(file, url, progress, complete, error, scope, asPut) {
            if (!this.isFileSizeAllowed([file])) {
                this._onUnableToUploadError(this.largeFileWarningText, error);
                return;
            }
            if (this.isHTML5Supported()) {
                this._uploadFileHTML5_asBinary(file, url, progress, complete, error, scope, asPut);
            } else {
                this._onUnableToUploadError(this.unableToUploadText, error);
            }
        },
        _uploadFileHTML5_asBinary: function(file, url, progress, complete, error, scope, asPut) {
            window.BlobBuilder = window.BlobBuilder ||
                         window.WebKitBlobBuilder ||
                         window.MozBlobBuilder ||
                         window.MSBlobBuilder;
            if (!url) {
                //assume Attachment SData url
                url = 'slxdata.ashx/slx/system/-/attachments/file';// TODO: Remove this assumption from SDK
            }

            var request = new XMLHttpRequest(), service = App.getService(), reader;
            request.open((asPut) ? 'PUT' : 'POST', url);
            request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

            if (service) {
                request.setRequestHeader('Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization-Mode', 'no-challenge');
            }

            reader = new FileReader();
            reader.onload = lang.hitch(this, function(evt) {
                var binary, boundary, dashdash, crlf, bb, unknownErrorText, usingBlobBuilder, blobReader, blobData;
                unknownErrorText = this.unknownErrorText;
                blobReader = new FileReader();// read the blob as an ArrayBuffer to work around this android issue: https://code.google.com/p/android/issues/detail?id=39882

                try {
                    new Blob();// This will throw an exception if it is no supported (android)
                    bb = [];
                } catch(e) {
                    bb = new window.BlobBuilder();
                    usingBlobBuilder = true;
                }

                binary = evt.target.result;
                boundary = "---------------------------" + (new Date()).getTime();
                dashdash = '--';
                crlf = '\r\n';

                this._append(bb, dashdash + boundary + crlf);
                this._append(bb, 'Content-Disposition: attachment; ');
                this._append(bb, 'name="file_"; ');
                this._append(bb, 'filename*="' + encodeURI(file.name) + '" ');
                this._append(bb, crlf);
                this._append(bb, 'Content-Type: ' + file.type);
                this._append(bb, crlf + crlf);
                this._append(bb, binary);
                this._append(bb, crlf);
                this._append(bb, dashdash + boundary + dashdash + crlf);

                if (complete) {
                    request.onreadystatechange = function() {
                        if (request.readyState === 4) {
                            if (Math.floor(request.status / 100) !== 2) {
                                if (error) {
                                    error.call(scope || this, unknownErrorText);
                                    console.warn(unknownErrorText + " "+ request.responseText);
                                }
                            } else {
                                complete.call(scope || this, request);
                            }
                        }
                    };
                }

                if (progress) {
                    request.upload.addEventListener('progress', function(e) {
                        progress.call(scope || this, e);
                    });
                }

                request.setRequestHeader('Content-Type', 'multipart/attachment; boundary=' + boundary);

                if (usingBlobBuilder) {
                    blobData = bb.getBlob(file.type);
                } else {
                    blobData = new Blob(bb);
                }

                // Read the blob back as an ArrayBuffer to work around this android issue:
                // https://code.google.com/p/android/issues/detail?id=39882
                blobReader.onload = function(e) {
                    request.send(e.target.result);
                }

                blobReader.readAsArrayBuffer(blobData);
            });

            reader.readAsArrayBuffer(file);
        },
        _append: function(arrayOrBlobBuilder, data) {
            if (arrayOrBlobBuilder && arrayOrBlobBuilder.constructor === Array) {
                arrayOrBlobBuilder.push(data);
            } else {
                arrayOrBlobBuilder.append(data);
            }
        },
        _onUnableToUploadError: function(msg, onError) {
            if (!msg) {
                msg = this.unableToUploadText
            }
            if (onError) {
                onError([msg])
            } else {
                console.warn([msg]);
            }
        },
        formatFileSize: function(size) {
            size = parseInt(size, 10);
            if (size === 0) {
                return '0 KB';
            }
            if (!size || size < 0) {
                return this.unknownSizeText;
            }
            if (size < 1024) {
                return '1 KB';
            }
            return dNumber.format(Math.round(size / 1024)) + ' KB';
        },
        getFile: function(fileUrl, responseType , onSuccess) {
            var request, service, self;

            request = new XMLHttpRequest();
            service = App.getService();
            self = this;

            request.open("GET", fileUrl, true);

            if (responseType) {
                request.responseType = responseType;
            }
            request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

            if (service) {
                request.setRequestHeader('Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
                request.setRequestHeader('X-Authorization-Mode', 'no-challenge');
            }
            request.addEventListener("load", function() {
                var data, contentType, contentInfo, responseInfo, fileName;

                data = this.response;
                contentType = this.getResponseHeader("Content-Type");
                contentInfo = this.getResponseHeader("Content-Disposition");
                responseInfo = {};
                fileName = contentInfo.split('=')[1];

                responseInfo = {
                    request: this,
                    responseType: responseType,
                    response: this.response,
                    contentType: contentType,
                    fileName: fileName
                };
                if (onSuccess) {
                    onSuccess(responseInfo);
                }
            }, false);
            request.send(null);
        }
    });
});
<|endoftext|>


<commit_before>var requirejs = require('requirejs'),
    jsdom = require('jsdom').jsdom,
    d3 = require('d3'),
    fs = require('fs'),
    expect = require('chai').expect;

requirejs.config({
  baseUrl: '',
  paths: {
    reactivis: 'dist/reactivis',
    model: 'bower_components/model/dist/model'
  },
  nodeRequire: require
});

function outputDataFlowGraph(name, model){
  model.detectFlowGraph(function (graph) {
    var json = JSON.stringify(graph, null, 2);
    fs.writeFile('./dataFlowGraphs/' + name + '.json', json, function(err) {
      if(err) console.log(err);
    });
  });
}

describe('A suite', function() {
  var reactivis = requirejs('reactivis'),
      Model = requirejs('model'),
      document = jsdom('');

  function createDiv(){
    var div = document.createElement('div');
    document.body.appendChild(div);
    return div;
  }

  function test(name, callback){
    var model = Model();
    outputDataFlowGraph(name, model);
    reactivis(model)[name]();
    it(name, function(done) {
      callback(model, done);
    });
  }

  test('svg', function (model, done) {
    model.set('div', createDiv());
    model.when(['svg', 'g'], function(svg, g) {
      expect(svg.node().nodeName).to.equal('SVG');
      expect(g.node().nodeName).to.equal('G');
      setTimeout(function () {
        model.set('box', {
          x: 50,
          y: 50,
          width: 200,
          height: 250
        });
        setTimeout(function () {
          expect(svg.attr('width')).to.equal('200');
          expect(svg.attr('height')).to.equal('250');
          // todo test x, y
          done();
        }, 0);
      }, 0);
    });

  });
  test('margin', function (model, done) {
    model.set({
      box: { x: 50, y: 50, width: 200, height: 250 },
      margin: { top: 10, right: 20, bottom: 30, left: 40 }
    });
    model.when(['width', 'height'], function(width, height) {
      expect(width).to.equal(200 - 40 - 20);
      expect(height).to.equal(250 - 10 - 30);
      done();
    });
  });

  test('xLinearScale', function (model, done) {
    model.set({
      data: [ 3, 4, -1, 5 ],
      getX: function (d) { return d; }
    });
    model.when('xDomain', function (xDomain) {
      expect(xDomain[0]).to.equal(-1);
      expect(xDomain[1]).to.equal(5);

      setTimeout(function () {
        model.set('width', 500);
        model.when('xRange', function (xRange) {
          expect(xRange[0]).to.equal(0);
          expect(xRange[1]).to.equal(500);
          done();
        });
      }, 0);
    });
  });

  test('xOrdinalScale', function (model, done) {
    model.set({
      data: [ 'A', 'B', 'C' ],
      getX: function (d) { return d; }
    });
    model.when('xDomain', function (xDomain) {
      expect(xDomain[0]).to.equal('A');
      expect(xDomain[2]).to.equal('C');

      setTimeout(function () {
        model.set('width', 500);
        model.when('xRange', function (xRange) {
          expect(xRange[2]).to.equal(339);
          done();
        });
      }, 0);
    });
  });
});
<commit_msg>Added unit tests<commit_after>var requirejs = require('requirejs'),
    jsdom = require('jsdom').jsdom,
    d3 = require('d3'),
    fs = require('fs'),
    expect = require('chai').expect;

requirejs.config({
  baseUrl: '',
  paths: {
    reactivis: 'dist/reactivis',
    model: 'bower_components/model/dist/model'
  },
  nodeRequire: require
});

function outputDataFlowGraph(name, model){
  model.detectFlowGraph(function (graph) {
    var json = JSON.stringify(graph, null, 2);
    fs.writeFile('./dataFlowGraphs/' + name + '.json', json, function(err) {
      if(err) console.log(err);
    });
  });
}

describe('A suite', function() {
  var reactivis = requirejs('reactivis'),
      Model = requirejs('model'),
      document = jsdom('');

  function createDiv(){
    var div = document.createElement('div');
    document.body.appendChild(div);
    return div;
  }

  function test(name, callback){
    var model = Model();
    outputDataFlowGraph(name, model);
    reactivis(model)[name]();
    it(name, function(done) {
      callback(model, done);
    });
  }

  test('svg', function (model, done) {
    model.set('div', createDiv());
    model.when(['svg', 'g'], function(svg, g) {
      expect(svg.node().nodeName).to.equal('SVG');
      expect(g.node().nodeName).to.equal('G');
      setTimeout(function () {
        model.set('box', {
          x: 50,
          y: 50,
          width: 200,
          height: 250
        });
        setTimeout(function () {
          expect(svg.attr('width')).to.equal('200');
          expect(svg.attr('height')).to.equal('250');
          // todo test x, y
          done();
        }, 0);
      }, 0);
    });

  });
  test('margin', function (model, done) {
    model.set({
      box: { x: 50, y: 50, width: 200, height: 250 },
      margin: { top: 10, right: 20, bottom: 30, left: 40 }
    });
    model.when(['width', 'height'], function(width, height) {
      expect(width).to.equal(200 - 40 - 20);
      expect(height).to.equal(250 - 10 - 30);
      done();
    });
  });

  test('xLinearScale', function (model, done) {
    model.set({
      data: [ 3, 4, -1, 5 ],
      getX: function (d) { return d; }
    });
    model.when('xDomain', function (xDomain) {
      expect(xDomain[0]).to.equal(-1);
      expect(xDomain[1]).to.equal(5);

      setTimeout(function () {
        model.set('width', 500);
        model.when('xRange', function (xRange) {
          expect(xRange[0]).to.equal(0);
          expect(xRange[1]).to.equal(500);
          done();
        });
      }, 0);
    });
  });

  test('xOrdinalScale', function (model, done) {
    model.set({
      data: [ 'A', 'B', 'C' ],
      getX: function (d) { return d; }
    });
    model.when('xDomain', function (xDomain) {
      expect(xDomain[0]).to.equal('A');
      expect(xDomain[2]).to.equal('C');

      setTimeout(function () {
        model.set('width', 500);
        model.when('xRange', function (xRange) {
          expect(xRange[2]).to.equal(339);
          done();
        });
      }, 0);
    });
  });

  it('xAxis', function(done) {
    var model = Model();

    reactivis(model)
      .svg()
      .margin()
      .xLinearScale();

    model.set({
      div: createDiv(),
      box: { x: 50, y: 50, width: 200, height: 250 },
      margin: { top: 10, right: 20, bottom: 30, left: 40 },
      data: [ 3, 4, -1, 5 ],
      getX: function (d) { return d; }
    });

    model.when(['xScale', 'g'], function () {
      setTimeout(function () {
        outputDataFlowGraph('xAxis', model);
        reactivis(model).xAxis();
        model.when(['xAxis', 'xAxisG'], function (xAxis, xAxisG) {
          expect(xAxis.scale()).to.equal(model.get('xScale'));
          expect(xAxisG.node().nodeName).to.equal('G');
          done();
        });
      }, 0);
    });
  });

  it('xAxisLabel', function(done) {
    var model = Model();

    reactivis(model)
      .svg()
      .margin()
      .xLinearScale()
      .xAxis();

    model.set({
      div: createDiv(),
      box: { x: 50, y: 50, width: 200, height: 250 },
      margin: { top: 10, right: 20, bottom: 30, left: 40 },
      data: [ 3, 4, -1, 5 ],
      getX: function (d) { return d; }
    });

    model.when(['xAxis', 'xAxisG'], function (xAxis, xAxisG) {
      setTimeout(function () {
        outputDataFlowGraph('xAxisLabel', model);
        reactivis(model).xAxisLabel();
        model.set('xLabel', 'Population');
        model.when('xAxisLabel', function (xAxisLabel) {
          expect(xAxisLabel.text()).to.equal('Population');
          done();
        });
      }, 0);
    });
  });
});
<|endoftext|>


<commit_before>import classnames from "classnames";
import omit from "lodash.omit";

// This code borrowed with permission from f.lux-react and then modified to match
// solidify-flux differences

import PropTypes from 'prop-types';

import React, { Component } from "react";
import result from "lodash.result";

import baseModel from "./baseModel";
import identity from "./identity";

const typeSpec = {
	errors: PropTypes.object
};

const propNamesBlacklist = [
	'bind',
	'checkedType',
	'children',
	'disabled',
	'flushOnChange',
	'flushOnEnter',
	'format',
	'model',
	'onBlur',
	'onChange',
	'onChangeTx',
	'onUpdate',
	'parse',
	'ref',
	'value',
];

function defaultChangeTx(callback) {
	return function(event) {
		callback(event)
	}
}

/**
	A component that binds to a Flux model property to a wrapped component. This can be used with
	the built-in <input> tag or a custom component that supports the normal React bound component
	conventions.

	Example:
		<FluxWrapper
				model={ person }
				bind="dob"
				className="form-control name"
			>
				<MaskedInput
					mask="dd?//dd?//dddd"
					placeholder="mm/dd/yyyy"
				/>
		</FluxWrapper>

	This component accepts the following flux related properties:
		model - the Flux model (required)
		bind - the name of the flux property the component exposes (required)
		className - specify the classes for the wrapped compnent on THIS component and NOT the wrapped
			component. This quirk is required so this component can set the 'error' class when a
			validation error is present on the context.
		format - translation function called when the model[bind] value changes to convert the model
			value to a format suitable for viewing. Useful for converting values for localization
			purposes, such as dates, numbers, currencies. The function signature is:

				function format(value) { return value }

			Default value is the identity function.
		parse - translation function called when setting the model[bind] value to convert a displayed
			value to proper format for storage. Function signature is same as the format property.
			This method can throw a RangeError if the value is not valid. The default value is the
			identity function.
		checkedType - boolean flag to specify if the model[bind] value should be converted to a
			'checked' property for the wrapped component. Set to true for <input type='checkbox|radio'>
			or other components utilizing a checked property.  Default is false.
		flushOnChange - boolean flag specifying if the model[bind] value should be updated on each
			input change. The value will always be flushed on a blur event. This value is treated as
			set when the 'checkedType' property is set to true. The default is false.
		flushOnEnter - boolean flag specifying if model[bind] value should be set when the return
			key is pressed. Default is true.
		onChangeTx - onChange() event handler for converting non-React standard onChange() callback
			to standard React format, ie callback(event). The event object should at a minimum support
			the value and checked properties (as appropriate) and the persist() method. The function
			signature is:

				function changeTx(callback) {
					return function(dataFromComponent) {
						callback(new MyCustomEvevnt(dataFromComponent))
					}
				}

			Thus, translation function takes a callback from the FluxWrapper and returns a function
			that will be passed to the wrapped compnent's 'onChange' property. The 'onChange' handler
			then converts the onChange event data to a React-like event and invokes the callback.
			(Optional)

	Disabled behavior
		The disabled property may be set on this component. The wrapped component will receive a
		disabled property set to true if the 'disabled' property is explicitly specified or the
		model.$().isReadonly() method return true.

	'className' property handling
		DO NOT set the 'className' property on the wrapped component. Instead set it on this component
		and it will be passed to the wrapped component. This convention allows the FluxWrapper to add
		the 'error' class to the wrapped compnents 'className' properties when a validation error is
		present on the context.
*/
export default class FluxWrapper extends Component {
	constructor(props, context) {
		super(props, context);

		this.state = {
				error: false,
				value: this._formattedPropValue(),
			}
	}

	get value() {
		const { parse } = this.props;
		const { value } = this.state;

		return parse(value);
	}

	componentWillReceiveProps(nextProps, nextContext) {
		const modelValue = this._modelValue(nextProps);
		const prevModelValue = this._modelValue();
		const access = nextProps.model && nextProps.model.$();

		if (modelValue !== prevModelValue &&
			(
				this.flushValueSet && modelValue !== this.flushValue ||
				(access && access.isDirty && !access.isDirty())
			))
		{

			this.setState({
					value: this._formattedPropValue(nextProps),
				});
		}

		delete this.flushValue;
		delete this.flushValueSet;
	}

	getChildContext () {
		var context = this.context;
		var props = this.props;

		return {
			errors: props.errors || context.errors || this.state.errors,
		};
	}

	flush() {
		return new Promise( (resolve, reject) => {
			this.flushValue = this._updateValue( (currModel, error) => {
				if (error) {
					reject(error);
				} else {
					resolve(model);
				}
			});

			this.flushValueSet = true;
		});
	}

	_formattedPropValue(props=this.props) {
		const { format } = props;
		const modelValue = this._modelValue(props);

		return format(modelValue);
	}

	_handleBlur(event) {
		const { onBlur } = this.props;

		this.setState({
				hasFocus: false,
				focusValue: null,
			},
			() => {
				this._updateValue();

				onBlur && onBlur(event)
			}
		);
	}

	_handleChange(event) {
		const { format, onChange, type } = this.props;
		const { checked, value } = event.target;
		const nextModelValue = this._isCheckedType() ?checked :value;

		// Cannot reuse event as noted below but keep for now (1/7/2017)
		// if (onChange) {
		// 	event.persist();

		// 	event.target.value = nextModelValue;
		// }

		this.setState(
			{
				value: nextModelValue,
			},
			() => {
				if (onChange) {
					// this is causing the cursor to jump to end of input (strange)
					//event.target.value = nextModelValue;

					// create a really shallow and dumb event (should meet our needs for now). Really
					// need to create an actual event to propagate.
					onChange({
						target: {
							value: nextModelValue
						}
					})
				}

				if (this._shouldFlushOnChange()) {
					this._updateValue();
				}
			});
	}

	_handleFocus(event) {
		const { onFocus } = this.props;

		this.setState(
			{
				hasFocus: true,
				focusValue: this._modelValue(),    // will compare on blur to see if value changed
			},
			() => onFocus && onFocus(event)
		);
	}

	_handleKeyPress(event) {
		const { flushOnEnter, onKeyPress } = this.props;

		if (event.charCode === 13 && flushOnEnter) {
			this._updateValue();
		}

		onKeyPress && onKeyPress(event);
	}

	_hasError() {
		const { error } = this.state;
		const { errors } = this.context;
		const { bind, model } = this.props;
		const { target, key } = baseModel(model, bind);

		return error || (errors && errors.hasPath(target, key));
	}

	_isCheckedType() {
		const { checkedType } = this.props;

		return checkedType;
	}

	_modelValue(props=this.props) {
		const { model, bind } = props;

		return result(model, bind);
	}

	_shouldFlushOnChange() {
		const { flushOnChange, type } = this.props;

		return flushOnChange || (flushOnChange == false && this._isCheckedType());
	}

	_updateError(msg, error) {
		const { bind, model } = this.props;
		const { hasFocus } = this.state;
		const wrappedValue = this.wrapped && this.wrapped.value;

		if (process.env.NODE_ENV !== 'production') {
			console.warn(`FluxWrapper update error: path=${model.$().dotPath()}, ` +
					`property=${bind}, ${msg}`, error);

			//alert("Invalid value - input reset.\n\nSee console for details.")
		}

		// reset the managed value to last good property value
		this.setState({
			error: true,
			value: hasFocus
				?wrappedValue
				:this._formattedPropValue()
		})

	}

	_updateValue(callback) {
		const { bind, model, onUpdate, parse } = this.props;
		const { value } = this.state;
		const { target, key } = baseModel(model, bind);
		var nextModelValue;

		try {
			nextModelValue = parse(value);
		} catch(ex) {
			return this._updateError(`parse() error - value=${value}`, ex);
		}

		try {
			if (target[key] !== nextModelValue) {
				// update the model property
				try {
					target[key] = nextModelValue;
				} catch(ex) {
					return this._updateError(`assign error - nextModelValue=${nextModelValue}`, ex);
				}

				this.setState({ error: false })

				if (onUpdate || callback) {
					model.$().waitFor( currModel => {
						if (callback) {
							try {
								callback(currModel);
							} catch(cbError) {
								console.warn(`FluxInput Update Callback Error: name=${bind}, path=${model.$().dotPath()}`, cbError);
							}
						}

						if (onUpdate) {
							try {
								onUpdate(currModel, bind);
							} catch(cbError) {
								console.warn(`FluxInput onUpdate() Error: name=${bind}, path=${model.$().dotPath()}`, cbError);
							}
						}
					});
				}
			}
		} catch(error) {
			console.warn(`FluxInput Update Error: name=${bind}, path=${model.$().dotPath()}`, value, error);

			// ensure browser state does not display the bogus change in place
			this.forceUpdate();

			callback && callback(model, error);
		}

		return nextModelValue;
	}

	render() {
		const { children, disabled, format, model, onChangeTx } = this.props;
		const className = classnames(this.props.className || '', {
				'error': this._hasError()
			});
		const childProps = { ...omit(this.props, propNamesBlacklist), className };
		const { value } = this.state;

		childProps.ref = ref => this.wrapped = ref;
		childProps.disabled = model.$().isReadonly() || disabled;
		childProps.onBlur = event => this._handleBlur(event)
		childProps.onChange = onChangeTx( event => this._handleChange(event) )
//		childProps.onChange = event => this._handleChange(event)
		childProps.onFocus =event => this._handleFocus(event)
		childProps.onKeyPress = event => this._handleKeyPress(event)
		childProps.value = value === undefined ?null :value;

		if (this._isCheckedType()) {
			childProps.checked = !!value;
		}

		return React.cloneElement(children, childProps);
	}
}

FluxWrapper.contextTypes = typeSpec;
FluxWrapper.childContextTypes = typeSpec;

FluxWrapper.defaultProps = {
	flushOnChange: false,
	flushOnEnter: true,
	format: identity,
	onChangeTx: identity,
	parse: identity,
}

FluxWrapper.propTypes = {
	bind: PropTypes.oneOfType([
			  PropTypes.string,
			  PropTypes.number
		  ]).isRequired,
	children: PropTypes.element.isRequired,
	checkedType: PropTypes.bool,
	disabled: PropTypes.bool,
	flushOnChange: PropTypes.bool,
	flushOnEnter: PropTypes.bool,
	format: PropTypes.func,
	model: PropTypes.object.isRequired,
	onBlur: PropTypes.func,
	onChange: PropTypes.func,
	onChangeTx: PropTypes.func,
	onError: PropTypes.func,
	onUpdate: PropTypes.func,
	parse: PropTypes.func,
}

<commit_msg>Update wrapped value when component is not the focus<commit_after>import classnames from "classnames";
import omit from "lodash.omit";

// This code borrowed with permission from f.lux-react and then modified to match
// solidify-flux differences

import PropTypes from 'prop-types';

import React, { Component } from "react";
import result from "lodash.result";

import baseModel from "./baseModel";
import identity from "./identity";

const typeSpec = {
	errors: PropTypes.object
};

const propNamesBlacklist = [
	'bind',
	'checkedType',
	'children',
	'disabled',
	'flushOnChange',
	'flushOnEnter',
	'format',
	'model',
	'onBlur',
	'onChange',
	'onChangeTx',
	'onUpdate',
	'parse',
	'ref',
	'value',
];

function defaultChangeTx(callback) {
	return function(event) {
		callback(event)
	}
}

/**
	A component that binds to a Flux model property to a wrapped component. This can be used with
	the built-in <input> tag or a custom component that supports the normal React bound component
	conventions.

	Example:
		<FluxWrapper
				model={ person }
				bind="dob"
				className="form-control name"
			>
				<MaskedInput
					mask="dd?//dd?//dddd"
					placeholder="mm/dd/yyyy"
				/>
		</FluxWrapper>

	This component accepts the following flux related properties:
		model - the Flux model (required)
		bind - the name of the flux property the component exposes (required)
		className - specify the classes for the wrapped compnent on THIS component and NOT the wrapped
			component. This quirk is required so this component can set the 'error' class when a
			validation error is present on the context.
		format - translation function called when the model[bind] value changes to convert the model
			value to a format suitable for viewing. Useful for converting values for localization
			purposes, such as dates, numbers, currencies. The function signature is:

				function format(value) { return value }

			Default value is the identity function.
		parse - translation function called when setting the model[bind] value to convert a displayed
			value to proper format for storage. Function signature is same as the format property.
			This method can throw a RangeError if the value is not valid. The default value is the
			identity function.
		checkedType - boolean flag to specify if the model[bind] value should be converted to a
			'checked' property for the wrapped component. Set to true for <input type='checkbox|radio'>
			or other components utilizing a checked property.  Default is false.
		flushOnChange - boolean flag specifying if the model[bind] value should be updated on each
			input change. The value will always be flushed on a blur event. This value is treated as
			set when the 'checkedType' property is set to true. The default is false.
		flushOnEnter - boolean flag specifying if model[bind] value should be set when the return
			key is pressed. Default is true.
		onChangeTx - onChange() event handler for converting non-React standard onChange() callback
			to standard React format, ie callback(event). The event object should at a minimum support
			the value and checked properties (as appropriate) and the persist() method. The function
			signature is:

				function changeTx(callback) {
					return function(dataFromComponent) {
						callback(new MyCustomEvevnt(dataFromComponent))
					}
				}

			Thus, translation function takes a callback from the FluxWrapper and returns a function
			that will be passed to the wrapped compnent's 'onChange' property. The 'onChange' handler
			then converts the onChange event data to a React-like event and invokes the callback.
			(Optional)

	Disabled behavior
		The disabled property may be set on this component. The wrapped component will receive a
		disabled property set to true if the 'disabled' property is explicitly specified or the
		model.$().isReadonly() method return true.

	'className' property handling
		DO NOT set the 'className' property on the wrapped component. Instead set it on this component
		and it will be passed to the wrapped component. This convention allows the FluxWrapper to add
		the 'error' class to the wrapped compnents 'className' properties when a validation error is
		present on the context.
*/
export default class FluxWrapper extends Component {
	constructor(props, context) {
		super(props, context);

		this.state = {
				error: false,
				value: this._formattedPropValue(),
			}
	}

	get value() {
		const { parse } = this.props;
		const { value } = this.state;

		return parse(value);
	}

	componentWillReceiveProps(nextProps, nextContext) {
		const modelValue = this._modelValue(nextProps);
		const prevModelValue = this._modelValue();
		const access = nextProps.model && nextProps.model.$();

		if (modelValue !== prevModelValue &&
			(
				this.flushValueSet && modelValue !== this.flushValue ||
				(access && access.isDirty && !access.isDirty()) ||
				!this.state.focus
			))
		{

			this.setState({
					value: this._formattedPropValue(nextProps),
				});
		}

		delete this.flushValue;
		delete this.flushValueSet;
	}

	getChildContext () {
		var context = this.context;
		var props = this.props;

		return {
			errors: props.errors || context.errors || this.state.errors,
		};
	}

	flush() {
		return new Promise( (resolve, reject) => {
			this.flushValue = this._updateValue( (currModel, error) => {
				if (error) {
					reject(error);
				} else {
					resolve(model);
				}
			});

			this.flushValueSet = true;
		});
	}

	_formattedPropValue(props=this.props) {
		const { format } = props;
		const modelValue = this._modelValue(props);

		return format(modelValue);
	}

	_handleBlur(event) {
		const { onBlur } = this.props;

		this.setState({
				hasFocus: false,
				focusValue: null,
			},
			() => {
				this._updateValue();

				onBlur && onBlur(event)
			}
		);
	}

	_handleChange(event) {
		const { format, onChange, type } = this.props;
		const { checked, value } = event.target;
		const nextModelValue = this._isCheckedType() ?checked :value;

		// Cannot reuse event as noted below but keep for now (1/7/2017)
		// if (onChange) {
		// 	event.persist();

		// 	event.target.value = nextModelValue;
		// }

		this.setState(
			{
				value: nextModelValue,
			},
			() => {
				if (onChange) {
					// this is causing the cursor to jump to end of input (strange)
					//event.target.value = nextModelValue;

					// create a really shallow and dumb event (should meet our needs for now). Really
					// need to create an actual event to propagate.
					onChange({
						target: {
							value: nextModelValue
						}
					})
				}

				if (this._shouldFlushOnChange()) {
					this._updateValue();
				}
			});
	}

	_handleFocus(event) {
		const { onFocus } = this.props;

		this.setState(
			{
				hasFocus: true,
				focusValue: this._modelValue(),    // will compare on blur to see if value changed
			},
			() => onFocus && onFocus(event)
		);
	}

	_handleKeyPress(event) {
		const { flushOnEnter, onKeyPress } = this.props;

		if (event.charCode === 13 && flushOnEnter) {
			this._updateValue();
		}

		onKeyPress && onKeyPress(event);
	}

	_hasError() {
		const { error } = this.state;
		const { errors } = this.context;
		const { bind, model } = this.props;
		const { target, key } = baseModel(model, bind);

		return error || (errors && errors.hasPath(target, key));
	}

	_isCheckedType() {
		const { checkedType } = this.props;

		return checkedType;
	}

	_modelValue(props=this.props) {
		const { model, bind } = props;

		return result(model, bind);
	}

	_shouldFlushOnChange() {
		const { flushOnChange, type } = this.props;

		return flushOnChange || (flushOnChange == false && this._isCheckedType());
	}

	_updateError(msg, error) {
		const { bind, model } = this.props;
		const { hasFocus } = this.state;
		const wrappedValue = this.wrapped && this.wrapped.value;

		if (process.env.NODE_ENV !== 'production') {
			console.warn(`FluxWrapper update error: path=${model.$().dotPath()}, ` +
					`property=${bind}, ${msg}`, error);

			//alert("Invalid value - input reset.\n\nSee console for details.")
		}

		// reset the managed value to last good property value
		this.setState({
			error: true,
			value: hasFocus
				?wrappedValue
				:this._formattedPropValue()
		})

	}

	_updateValue(callback) {
		const { bind, model, onUpdate, parse } = this.props;
		const { value } = this.state;
		const { target, key } = baseModel(model, bind);
		var nextModelValue;

		try {
			nextModelValue = parse(value);
		} catch(ex) {
			return this._updateError(`parse() error - value=${value}`, ex);
		}

		try {
			if (target[key] !== nextModelValue) {
				// update the model property
				try {
					target[key] = nextModelValue;
				} catch(ex) {
					return this._updateError(`assign error - nextModelValue=${nextModelValue}`, ex);
				}

				this.setState({ error: false })

				if (onUpdate || callback) {
					model.$().waitFor( currModel => {
						if (callback) {
							try {
								callback(currModel);
							} catch(cbError) {
								console.warn(`FluxInput Update Callback Error: name=${bind}, path=${model.$().dotPath()}`, cbError);
							}
						}

						if (onUpdate) {
							try {
								onUpdate(currModel, bind);
							} catch(cbError) {
								console.warn(`FluxInput onUpdate() Error: name=${bind}, path=${model.$().dotPath()}`, cbError);
							}
						}
					});
				}
			}
		} catch(error) {
			console.warn(`FluxInput Update Error: name=${bind}, path=${model.$().dotPath()}`, value, error);

			// ensure browser state does not display the bogus change in place
			this.forceUpdate();

			callback && callback(model, error);
		}

		return nextModelValue;
	}

	render() {
		const { children, disabled, format, model, onChangeTx } = this.props;
		const className = classnames(this.props.className || '', {
				'error': this._hasError()
			});
		const childProps = { ...omit(this.props, propNamesBlacklist), className };
		const { value } = this.state;

		childProps.ref = ref => this.wrapped = ref;
		childProps.disabled = model.$().isReadonly() || disabled;
		childProps.onBlur = event => this._handleBlur(event)
		childProps.onChange = onChangeTx( event => this._handleChange(event) )
//		childProps.onChange = event => this._handleChange(event)
		childProps.onFocus =event => this._handleFocus(event)
		childProps.onKeyPress = event => this._handleKeyPress(event)
		childProps.value = value === undefined ?null :value;

console.log("FLUX WRAPPER", this.props.bind, this.props.tracking, model && model[this.props.bind], childProps)
		if (this._isCheckedType()) {
			childProps.checked = !!value;
		}

		return React.cloneElement(children, childProps);
	}
}

FluxWrapper.contextTypes = typeSpec;
FluxWrapper.childContextTypes = typeSpec;

FluxWrapper.defaultProps = {
	flushOnChange: false,
	flushOnEnter: true,
	format: identity,
	onChangeTx: identity,
	parse: identity,
}

FluxWrapper.propTypes = {
	bind: PropTypes.oneOfType([
			  PropTypes.string,
			  PropTypes.number
		  ]).isRequired,
	children: PropTypes.element.isRequired,
	checkedType: PropTypes.bool,
	disabled: PropTypes.bool,
	flushOnChange: PropTypes.bool,
	flushOnEnter: PropTypes.bool,
	format: PropTypes.func,
	model: PropTypes.object.isRequired,
	onBlur: PropTypes.func,
	onChange: PropTypes.func,
	onChangeTx: PropTypes.func,
	onError: PropTypes.func,
	onUpdate: PropTypes.func,
	parse: PropTypes.func,
}

<|endoftext|>


<commit_before>// @flow

import React from 'react'
import '../sigma/layout.forceAtlas2'
import { embedProps } from './tools'

type State = {
	running: boolean,
	timer?: number,
	drawEdges?: ?boolean
};

type Props = {
	worker: boolean,
	barnesHutOptimize?: boolean,
	barnesHutTheta?: number,
	adjustSizes?: boolean,
	iterationsPerRender?: number,
	linLogMode: boolean,
	outboundAttractionDistribution?: boolean,
	edgeWeightInfluence?: number,
	scalingRatio?: number,
	strongGravityMode?: boolean,
	slowDown?: number,
	gravity?: number,
	timeout?: number,
	sigma?: sigma
};

type DefaultProps = {
	worker: boolean,
	linLogMode: boolean
};

/**

ForceAtlas2 component, starts ForceAtlas2 sigma plugin once component is mounted.
It supposes that sigma graph is already in place, therefore component should not be
mounted while graph is unavailable. It can be used within Sigma component if graph is
preloaded, or within loader component, like NeoCypher.

It accepts all the parameters of ForceAtlas2 described on its github page:
@param {boolean} [worker=true]           Use a web worker to run calculations in separate thread
@param {boolean} barnesHutOptimize  Use the algorithm's Barnes-Hut to improve repulsion's scalability
									This is useful for large graph but harmful to small ones.
@param {number} barnesHutTheta
@param {boolean} adjustSizes
@param {number} iterationsPerRender
@param {boolean} [linLogMode=true]
@param {boolean} outboundAttractionDistribution
@param {number} edgeWeightInfluence
@param {number} scalingRatio
@param {boolean} strongGravityMode
@param {number} gravity
@param {number} slowDown
@param {number} timeout   how long algorythm should run. default=graph.nodes().length * 10

[see sigma plugin page for more details](https://github.com/jacomyal/sigma.js/tree/master/plugins/sigma.layout.forceAtlas2)

**/

class ForceAtlas2 extends React.Component {
	state: State;
	props: Props;
	static defaultProps: DefaultProps = {
		worker: true,
		linLogMode: true
	}

	constructor(props: Props) {
		super(props)
		this.state = {running:false}
	}

	componentDidMount() {
		this._refreshGraph()
	}

	componentDidUpdate(prevProps: Props, prevState: State) {
		let s = this.props.sigma
		if(prevState.running && !this.state.running && s) {
				s.stopForceAtlas2()
				s.settings({drawEdges:prevState.drawEdges===false ? false : true})
				s.refresh();
		}
	}

	componentWillUnmount() {
		if(this.props.sigma) this.props.sigma.killForceAtlas2()
		if(this.state.timer) clearTimeout(this.state.timer)
	}

	render() {
        if (!this.state.running) {
            return
{ embedProps(this.props.children, {sigma: this.props.sigma}) }
;
        }
        return null;
    }

	_refreshGraph() {
		let s = this.props.sigma
		if(!sigma || !s) return

		let drawEdges = s.settings("drawEdges")
		if(s.graph.edges().length > 1000)
				s.settings({drawEdges: false})

		s.startForceAtlas2(this._stripOptions(this.props));
		// TODO: convert running status to state
		let timer = setTimeout(() => {
					this.setState({running:false, timer:undefined})
				}, this.props.timeout || s.graph.nodes().length*8 );
		this.setState({running:true, timer, drawEdges})
	}

	//strip force atlas options from component props
  _stripOptions(props: Props): Props {
        return Object.assign({}, props, {
            sigma: undefined,
						children: undefined
        })
    }

}

export default ForceAtlas2;
<commit_msg>Fixed whitespace<commit_after>// @flow

import React from 'react'
import '../sigma/layout.forceAtlas2'
import { embedProps } from './tools'

type State = {
	running: boolean,
	timer?: number,
	drawEdges?: ?boolean
};

type Props = {
	worker: boolean,
	barnesHutOptimize?: boolean,
	barnesHutTheta?: number,
	adjustSizes?: boolean,
	iterationsPerRender?: number,
	linLogMode: boolean,
	outboundAttractionDistribution?: boolean,
	edgeWeightInfluence?: number,
	scalingRatio?: number,
	strongGravityMode?: boolean,
	slowDown?: number,
	gravity?: number,
	timeout?: number,
	sigma?: sigma
};

type DefaultProps = {
	worker: boolean,
	linLogMode: boolean
};

/**

ForceAtlas2 component, starts ForceAtlas2 sigma plugin once component is mounted.
It supposes that sigma graph is already in place, therefore component should not be
mounted while graph is unavailable. It can be used within Sigma component if graph is
preloaded, or within loader component, like NeoCypher.

It accepts all the parameters of ForceAtlas2 described on its github page:
@param {boolean} [worker=true]           Use a web worker to run calculations in separate thread
@param {boolean} barnesHutOptimize  Use the algorithm's Barnes-Hut to improve repulsion's scalability
									This is useful for large graph but harmful to small ones.
@param {number} barnesHutTheta
@param {boolean} adjustSizes
@param {number} iterationsPerRender
@param {boolean} [linLogMode=true]
@param {boolean} outboundAttractionDistribution
@param {number} edgeWeightInfluence
@param {number} scalingRatio
@param {boolean} strongGravityMode
@param {number} gravity
@param {number} slowDown
@param {number} timeout   how long algorythm should run. default=graph.nodes().length * 10

[see sigma plugin page for more details](https://github.com/jacomyal/sigma.js/tree/master/plugins/sigma.layout.forceAtlas2)

**/

class ForceAtlas2 extends React.Component {
	state: State;
	props: Props;
	static defaultProps: DefaultProps = {
		worker: true,
		linLogMode: true
	}

	constructor(props: Props) {
		super(props)
		this.state = {running:false}
	}

	componentDidMount() {
		this._refreshGraph()
	}

	componentDidUpdate(prevProps: Props, prevState: State) {
		let s = this.props.sigma
		if(prevState.running && !this.state.running && s) {
				s.stopForceAtlas2()
				s.settings({drawEdges:prevState.drawEdges===false ? false : true})
				s.refresh();
		}
	}

	componentWillUnmount() {
		if(this.props.sigma) this.props.sigma.killForceAtlas2()
		if(this.state.timer) clearTimeout(this.state.timer)
	}

	render() {
        if (!this.state.running) {
            return
{ embedProps(this.props.children, {sigma: this.props.sigma}) }
;
        }
        return null;
    }

	_refreshGraph() {
		let s = this.props.sigma
		if(!sigma || !s) return

		let drawEdges = s.settings("drawEdges")
		if(s.graph.edges().length > 1000)
				s.settings({drawEdges: false})

		s.startForceAtlas2(this._stripOptions(this.props));
		// TODO: convert running status to state
		let timer = setTimeout(() => {
					this.setState({running:false, timer:undefined})
				}, this.props.timeout || s.graph.nodes().length*8 );
		this.setState({running:true, timer, drawEdges})
	}

	//strip force atlas options from component props
  _stripOptions(props: Props): Props {
        return Object.assign({}, props, {
            sigma: undefined,
            children: undefined
        })
    }

}

export default ForceAtlas2;
<|endoftext|>


<commit_before>import test from 'ava';
import { Resolver } from '../src/resolver';
import { HTTPScheme } from '../src/http-scheme';
import { URLMapperScheme } from '../src/url-mapper-scheme';
import { Context } from '../src/context';

import { URL } from 'url';

test('register schemes plain', t => {
  const resolver = new Resolver();
  const http = new HTTPScheme();
  resolver.registerScheme(http);

  t.is(resolver.schemeForURL(new URL('http://somewhere/')), http);
});

test('register schemes mapper', t => {
  const resolver = new Resolver();
  const heise = new URLMapperScheme(
    new HTTPScheme(),
    'heise',
    new URL('http://www.heise.de/')
  );
  resolver.registerScheme(heise);

  t.is(resolver.schemeForURL(new URL('heise:index.html')), heise);
  t.is(
    resolver.resolve(new URL('heise:index.html')).href,
    new URL('http://www.heise.de/index.html').href
  );
});

test('register schemes from config', t => {
  const resolver = new Resolver(
    {
      schemes: {
        tmp: {
          base: 'http',
          prefix: 'http:///tmp'
        }
      }
    },
    [HTTPScheme]
  );

  t.is(resolver.schemes.get('http').name, 'http');
  t.is(resolver.schemes.get('tmp').name, 'tmp');
});

test('handles unknown', t => {
  const resolver = new Resolver();
  t.is(resolver.schemeForURL(new URL('undefined:index.html')), undefined);
  t.is(
    resolver.resolve(new URL('undefined:index.html').href),
    new URL('undefined:index.html').href
  );
});

test('unknown reject get', async t => {
  const context = new Context();
  const resolver = new Resolver();

  const error = await t.throws(
    resolver.get(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject stat', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.stat(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject put', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.put(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject delete', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.delete(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject list', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.list(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject history', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.history(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test.cb('delegating can get', t => {
  const context = new Context();
  const resolver = new Resolver();
  const heise = new URLMapperScheme(
    new HTTPScheme(),
    'heise',
    new URL('http://www.heise.de/')
  );
  resolver.registerScheme(heise);

  t.plan(1);

  resolver.get(context, new URL('heise:index.html')).then(stream =>
    stream.on('data', chunk => {
      if (chunk.includes('DOCTYPE')) {
        t.pass();
        t.end();
      }
    })
  );
});
<commit_msg>test: reformat<commit_after>import test from 'ava';
import { Resolver } from '../src/resolver';
import { HTTPScheme } from '../src/http-scheme';
import { URLMapperScheme } from '../src/url-mapper-scheme';
import { Context } from '../src/context';
import { URL } from 'url';

test('register schemes plain', t => {
  const resolver = new Resolver();
  const http = new HTTPScheme();
  resolver.registerScheme(http);

  t.is(resolver.schemeForURL(new URL('http://somewhere/')), http);
});

test('register schemes mapper', t => {
  const resolver = new Resolver();
  const heise = new URLMapperScheme(
    new HTTPScheme(),
    'heise',
    new URL('http://www.heise.de/')
  );
  resolver.registerScheme(heise);

  t.is(resolver.schemeForURL(new URL('heise:index.html')), heise);
  t.is(
    resolver.resolve(new URL('heise:index.html')).href,
    new URL('http://www.heise.de/index.html').href
  );
});

test('register schemes from config', t => {
  const resolver = new Resolver(
    {
      schemes: {
        tmp: {
          base: 'http',
          prefix: 'http:///tmp'
        }
      }
    },
    [HTTPScheme]
  );

  t.is(resolver.schemes.get('http').name, 'http');
  t.is(resolver.schemes.get('tmp').name, 'tmp');
});

test('handles unknown', t => {
  const resolver = new Resolver();
  t.is(resolver.schemeForURL(new URL('undefined:index.html')), undefined);
  t.is(
    resolver.resolve(new URL('undefined:index.html').href),
    new URL('undefined:index.html').href
  );
});

test('unknown reject get', async t => {
  const context = new Context();
  const resolver = new Resolver();

  const error = await t.throws(
    resolver.get(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject stat', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.stat(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject put', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.put(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject delete', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.delete(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject list', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.list(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test('unknown reject history', async t => {
  const context = new Context();
  const resolver = new Resolver();
  const error = await t.throws(
    resolver.history(context, new URL('something:index.html'))
  );
  t.is(error.message, 'Unknown scheme something:index.html');
});

test.cb('delegating can get', t => {
  const context = new Context();
  const resolver = new Resolver();
  const heise = new URLMapperScheme(
    new HTTPScheme(),
    'heise',
    new URL('http://www.heise.de/')
  );
  resolver.registerScheme(heise);

  t.plan(1);

  resolver.get(context, new URL('heise:index.html')).then(stream =>
    stream.on('data', chunk => {
      if (chunk.includes('DOCTYPE')) {
        t.pass();
        t.end();
      }
    })
  );
});
<|endoftext|>


<commit_before>import React from 'react';
import { props, t } from '../utils';
import getUrl from './getUrl';

@props({
  src: t.String,
  width: t.maybe(t.Number),
  height: t.maybe(t.Number),
  quality: t.maybe(t.union([t.Number, t.enums.of(['auto'])])),
  format: t.maybe(t.enums.of(['jpeg', 'png', 'wdp', 'gif', 'auto']))
})
export default class Image extends React.Component {

  render() {
    const { src, width, height, quality, ...props } = this.props;
    const url = getUrl(src, { width, height, quality });

    return (
      <img {...props} src={url} />
    );
  }

}
<commit_msg>describe props<commit_after>import React from 'react';
import { props, t } from '../utils';
import getUrl from './getUrl';

const Props = {
  src: t.String,
  width: t.maybe(t.Number),
  height: t.maybe(t.Number),
  quality: t.maybe(t.union([t.Number, t.enums.of(['auto'])])),
  format: t.maybe(t.enums.of(['jpeg', 'png', 'wdp', 'gif', 'auto']))
};

/** A replacement for `<img>` to serve optimized images through a CDN in production
 * @param src - path to the image
 * @param width - used to resize the image to this width before downloading it
 * @param height - used to resize the image to this height before downloading it
 * @param quality - can be a number from 1 to 100 or "auto". Use "auto" to let cloudinary decide the quality for you
 * @param format - which format the image should be. Use "auto" to let cloudinary decide the format for you
 */
@props(Props)
export default class Image extends React.Component {

  render() {
    const { src, width, height, quality, ...props } = this.props;
    const url = getUrl(src, { width, height, quality });

    return (
      <img {...props} src={url} />
    );
  }

}
<|endoftext|>


<commit_before>/* @flow */
'use strict';

import type Options from './index';
import type Logger from './Logger';

export type ImageElement = Image | HTMLCanvasElement;
type ImageCache = {[string]: Promise<ImageElement>};

export default class ImageLoader {
    origin: string;
    options: Options;
    _link: HTMLAnchorElement;
    cache: ImageCache;
    logger: Logger;
    _index: number;

    constructor(options: Options, logger: Logger) {
        this.options = options;
        this.origin = this.getOrigin(window.location.href);
        this.cache = {};
        this.logger = logger;
        this._index = 0;
    }

    loadImage(src: string): ?string {
        if (this.hasImageInCache(src)) {
            return src;
        }

        if (this.options.allowTaint === true || this.isInlineImage(src) || this.isSameOrigin(src)) {
            return this.addImage(src, src);
        } else if (typeof this.options.proxy === 'string' && !this.isSameOrigin(src)) {
            // TODO proxy
        }
    }

    loadCanvas(node: HTMLCanvasElement): string {
        const key = String(this._index++);
        this.cache[key] = Promise.resolve(node);
        return key;
    }

    isInlineImage(src: string): boolean {
        return /data:image\/.*;base64,/i.test(src);
    }

    hasImageInCache(key: string): boolean {
        return typeof this.cache[key] !== 'undefined';
    }

    addImage(key: string, src: string): string {
        if (__DEV__) {
            this.logger.log(`Added image ${key.substring(0, 256)}`);
        }
        this.cache[key] = new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.onerror = reject;
            /*
            if (cors) {
             img.crossOrigin = 'anonymous';
            }
            */
            img.src = src;
            if (img.complete === true) {
                resolve(img);
            }
        });
        return key;
    }

    isSameOrigin(url: string): boolean {
        return this.getOrigin(url) === this.origin;
    }

    getOrigin(url: string): string {
        const link = this._link || (this._link = document.createElement('a'));
        link.href = url;
        link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
        return link.protocol + link.hostname + link.port;
    }

    ready(): Promise<ImageStore> {
        const keys = Object.keys(this.cache);
        return Promise.all(keys.map(str => this.cache[str])).then(images => {
            if (__DEV__) {
                this.logger.log('Finished loading images', images);
            }
            return new ImageStore(keys, images);
        });
    }
}

export class ImageStore {
    _keys: Array<string>;
    _images: Array<ImageElement>;

    constructor(keys: Array<string>, images: Array<ImageElement>) {
        this._keys = keys;
        this._images = images;
    }

    get(key: string): ?ImageElement {
        const index = this._keys.indexOf(key);
        return index === -1 ? null : this._images[index];
    }
}
<commit_msg>Allow image loads to fail without crashing render<commit_after>/* @flow */
'use strict';

import type Options from './index';
import type Logger from './Logger';

export type ImageElement = Image | HTMLCanvasElement;
type ImageCache = {[string]: Promise<ImageElement>};

export default class ImageLoader {
    origin: string;
    options: Options;
    _link: HTMLAnchorElement;
    cache: ImageCache;
    logger: Logger;
    _index: number;

    constructor(options: Options, logger: Logger) {
        this.options = options;
        this.origin = this.getOrigin(window.location.href);
        this.cache = {};
        this.logger = logger;
        this._index = 0;
    }

    loadImage(src: string): ?string {
        if (this.hasImageInCache(src)) {
            return src;
        }

        if (this.options.allowTaint === true || this.isInlineImage(src) || this.isSameOrigin(src)) {
            return this.addImage(src, src);
        } else if (typeof this.options.proxy === 'string' && !this.isSameOrigin(src)) {
            // TODO proxy
        }
    }

    loadCanvas(node: HTMLCanvasElement): string {
        const key = String(this._index++);
        this.cache[key] = Promise.resolve(node);
        return key;
    }

    isInlineImage(src: string): boolean {
        return /data:image\/.*;base64,/i.test(src);
    }

    hasImageInCache(key: string): boolean {
        return typeof this.cache[key] !== 'undefined';
    }

    addImage(key: string, src: string): string {
        if (__DEV__) {
            this.logger.log(`Added image ${key.substring(0, 256)}`);
        }
        this.cache[key] = new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.onerror = reject;
            /*
            if (cors) {
             img.crossOrigin = 'anonymous';
            }
            */
            img.src = src;
            if (img.complete === true) {
                resolve(img);
            }
        });
        return key;
    }

    isSameOrigin(url: string): boolean {
        return this.getOrigin(url) === this.origin;
    }

    getOrigin(url: string): string {
        const link = this._link || (this._link = document.createElement('a'));
        link.href = url;
        link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
        return link.protocol + link.hostname + link.port;
    }

    ready(): Promise<ImageStore> {
        const keys = Object.keys(this.cache);
        return Promise.all(keys.map(str => this.cache[str].catch(e => {
            if (__DEV__) {
                this.logger.log(`Unable to load image`, e);
            }
        }))).then(images => {
            if (__DEV__) {
                this.logger.log('Finished loading images', images);
            }
            return new ImageStore(keys, images);
        });
    }
}

export class ImageStore {
    _keys: Array<string>;
    _images: Array<ImageElement>;

    constructor(keys: Array<string>, images: Array<ImageElement>) {
        this._keys = keys;
        this._images = images;
    }

    get(key: string): ?ImageElement {
        const index = this._keys.indexOf(key);
        return index === -1 ? null : this._images[index];
    }
}
<|endoftext|>


<commit_before>/**
 * dashboard-api.js V0.1
 *
 * Copyright 2016, eMQTT
 *
 */
;
(function(win) {

	"use strict";

	// 取消缓存
	$.ajaxSetup({
		cache : false
	});

	var EF = function() {
	};

	var dApi = function() {
	};

	dApi.prototype = {

		// 配置项参数
		_config : {
			apiUri : "/"
		},

		// API初始化
		init : function(config) {
			$.extend(this._config, config || {});
		},

		_checkException : function() {
			if (this._config.apiUri == null) {
				throw new Error("Config 'apiUri' is null.");
			}
		},

		// var callback = function(ret, err) {};
		_ajax : function(apiEnd, data, callback) {
			this._checkException();
			$.ajax({
				type : "POST",
				url : this._config.apiUri + "api/" + apiEnd,
				dataType : "json",
				data : data,
				success : function(ret) {
					if ((apiEnd == 'remove_user'
						|| apiEnd == 'add_user')
						&& typeof ret == "object"
						&& ret.stauts == "failure") {
						callback(undefined, ret);
					} else {
						callback(ret, undefined);
					}
				},
				error : function(err) {
					callback(undefined, err);
				}
			});
		},

		// bnode
		bnode : function(callback) {
			this._ajax("bnode", null, callback);
		},

		// nodes
		nodes : function(callback) {
			this._ajax("node", null, callback);
		},

		// stats
		stats : function(callback) {
			this._ajax("stats", null, callback);
		},

		// metrics
		metrics : function(callback) {
			this._ajax("metrics", null, callback);
		},

		// listeners
		listeners : function(callback) {
			this._ajax("listeners", null, callback);
		},

		// clients
		clients : function(params, callback) {
			this._ajax("clients", params, callback);
		},

		// sessions
		sessions : function(callback) {
			this._ajax("sessions", null, callback);
		},

		// topics
		topics : function(callback) {
			this._ajax("topics", null, callback);
		},

		// subscriptions
		subscriptions : function(callback) {
			this._ajax("subscriptions", null, callback);
		},

		// users
		users : function(callback) {
			this._ajax("users", null, callback);
		},

		// user_remove
		user_remove : function(username, callback) {
			this._ajax("remove_user", {user_name : username}, callback);
		},

		// user_add
		user_add : function(user, callback) {
			this._ajax("add_user", user, callback);
		},

		// user_current
		user_current : function(callback) {
			this._ajax("current_user", null, callback);
		},

		// user_update
		user_update : function(callback) {
			this._ajax("update_user", null, callback);
		},

		// logout
		logout : function(callback) {
			this._ajax("logout", null, callback);
		},

		// routes
		routes : function(ret, err) {
			this._ajax("routes", null, callback);
		}
	};

	win.DashboardApi = dApi;

})(window);<commit_msg>routes api.<commit_after>/**
 * dashboard-api.js V0.1
 *
 * Copyright 2016, eMQTT
 *
 */
;
(function(win) {

	"use strict";

	// 取消缓存
	$.ajaxSetup({
		cache : false
	});

	var EF = function() {
	};

	var dApi = function() {
	};

	dApi.prototype = {

		// 配置项参数
		_config : {
			apiUri : "/"
		},

		// API初始化
		init : function(config) {
			$.extend(this._config, config || {});
		},

		_checkException : function() {
			if (this._config.apiUri == null) {
				throw new Error("Config 'apiUri' is null.");
			}
		},

		// var callback = function(ret, err) {};
		_ajax : function(apiEnd, data, callback) {
			this._checkException();
			$.ajax({
				type : "POST",
				url : this._config.apiUri + "api/" + apiEnd,
				dataType : "json",
				data : data,
				success : function(ret) {
					if ((apiEnd == 'remove_user'
						|| apiEnd == 'add_user')
						&& typeof ret == "object"
						&& ret.stauts == "failure") {
						callback(undefined, ret);
					} else {
						callback(ret, undefined);
					}
				},
				error : function(err) {
					callback(undefined, err);
				}
			});
		},

		// bnode
		bnode : function(callback) {
			this._ajax("bnode", null, callback);
		},

		// nodes
		nodes : function(callback) {
			this._ajax("node", null, callback);
		},

		// stats
		stats : function(callback) {
			this._ajax("stats", null, callback);
		},

		// metrics
		metrics : function(callback) {
			this._ajax("metrics", null, callback);
		},

		// listeners
		listeners : function(callback) {
			this._ajax("listeners", null, callback);
		},

		// clients
		clients : function(params, callback) {
			this._ajax("clients", params, callback);
		},

		// sessions
		sessions : function(callback) {
			this._ajax("sessions", null, callback);
		},

		// topics
		topics : function(callback) {
			this._ajax("topics", null, callback);
		},

		// subscriptions
		subscriptions : function(callback) {
			this._ajax("subscriptions", null, callback);
		},

		// users
		users : function(callback) {
			this._ajax("users", null, callback);
		},

		// user_remove
		user_remove : function(username, callback) {
			this._ajax("remove_user", {user_name : username}, callback);
		},

		// user_add
		user_add : function(user, callback) {
			this._ajax("add_user", user, callback);
		},

		// user_current
		user_current : function(callback) {
			this._ajax("current_user", null, callback);
		},

		// user_update
		user_update : function(callback) {
			this._ajax("update_user", null, callback);
		},

		// logout
		logout : function(callback) {
			this._ajax("logout", null, callback);
		},

		// routes
		routes : function(callback) {
			this._ajax("routes", null, callback);
		}
	};

	win.DashboardApi = dApi;

})(window);<|endoftext|>


<commit_before>/*globals initTestDB: false, emit: true, generateAdapterUrl: false */
/*globals PERSIST_DATABASES: false, initDBPair: false, utils: true */
/*globals ajax: true, LevelPouch: true */

"use strict";

var adapters = [
  ['local-1', 'http-1']
];
var qunit = module;
var LevelPouch;

if (typeof module !== undefined && module.exports) {
  Pouch = require('../src/pouch.js');
  LevelPouch = require('../src/adapters/pouch.leveldb.js');
  utils = require('./test.utils.js');

  for (var k in utils) {
    global[k] = global[k] || utils[k];
  }
  qunit = QUnit.module;
}

adapters.map(function(adapters) {

  qunit('replication + compaction', {
    setup: function() {
      this.local = generateAdapterUrl(adapters[0]);
      this.remote = generateAdapterUrl(adapters[1]);
    },
    teardown: function() {
      if (!PERSIST_DATABASES) {
        Pouch.destroy(this.local);
        Pouch.destroy(this.remote);
      }
    }
  });

  var doc = { _id: '0', integer: 0 };

  asyncTest('Testing issue #221', function() {
    var self = this;
    // Feature detection for pouchdb-server
    ajax({ url: 'http://localhost:2020/' }, function (err, ret) {
      if (!ret.couchdb) {
        ok(true, 'Test not applicable to backend.');
        return start();
      }
      // Create databases.
      initDBPair(self.local, self.remote, function(local, remote) {
        // Write a doc in CouchDB.
        remote.put(doc, {}, function(err, results) {
          // Update the doc.
          doc._rev = results.rev;
          doc.integer = 1;
          remote.put(doc, {}, function(err, results) {
            // Compact the db.
            remote.compact(function(data, status, jqXHR) {
              // Wait until compaction has affected the doc.
              var interval;
              var checkDoc = function() {
                remote.get(doc._id,{revs_info:true},function(err, data) {
                  var correctRev = data._revs_info[0];
                  if (data._revs_info[1].status === 'missing') {
                    // We already got a successful compaction, but did a whole
                    // new request before we figured it out, yay races
                    if (!interval) {
                      return;
                    }
                    clearInterval(interval);
                    interval = null;
                    // Replicate to PouchDB.
                    local.replicate.from(remote, function(err, results) {
                      // Check the PouchDB doc.
                      local.get(doc._id, function(err, results) {
                        ok(results._rev === correctRev.rev,
                           'correct rev stored after replication');
                        ok(results.integer === 1,
                           'correct content stored after replication');
                        start();
                      });
                    });
                  }
                });
              };
              interval = setInterval(checkDoc, 100);
            });
          });
        });
      });
    });
  });

  asyncTest('Testing issue #221 again', function() {
    var self = this;
    // Feature detection for pouchdb-server
    ajax({ url: 'http://localhost:2020/' }, function (err, ret) {
      if (!ret.couchdb) {
        ok(true, 'Test not applicable to backend.');
        return start();
      }
      // Create databases.
      initDBPair(self.local, self.remote, function(local, remote) {
        // Write a doc in CouchDB.
        remote.put(doc, {}, function(err, results) {
          doc._rev = results.rev;
          // Second doc so we get 2 revisions from replicate.
          remote.put(doc, {}, function(err, results) {
            doc._rev = results.rev;
            local.replicate.from(remote, function(err, results) {
              doc.integer = 1;
              // One more change
              remote.put(doc, {}, function(err, results) {
                // Testing if second replications fails now
                local.replicate.from(remote, function(err, results) {
                  local.get(doc._id, function(err, results) {
                    ok(results.integer === 1, 'correct content stored after replication');
                    start();
                  });
                });
              });
            });
          });
        });
      });
    });
  });
});
<commit_msg>(#578) - enable local adapters in test.issue211.js<commit_after>/*globals initTestDB: false, emit: true, generateAdapterUrl: false */
/*globals PERSIST_DATABASES: false, initDBPair: false, utils: true */
/*globals ajax: true, LevelPouch: true */

"use strict";

var adapters = [
  ['local-1', 'http-1'],
  ['http-1', 'http-2'],
  ['http-1', 'local-1'],
  ['local-1', 'local-2']
];
var qunit = module;
var LevelPouch;

if (typeof module !== undefined && module.exports) {
  Pouch = require('../src/pouch.js');
  LevelPouch = require('../src/adapters/pouch.leveldb.js');
  utils = require('./test.utils.js');

  for (var k in utils) {
    global[k] = global[k] || utils[k];
  }
  qunit = QUnit.module;
}

adapters.map(function(adapters) {

  qunit('replication + compaction: ' + adapters[0] + ':' + adapters[1], {
    setup: function() {
      this.local = generateAdapterUrl(adapters[0]);
      this.remote = generateAdapterUrl(adapters[1]);
    },
    teardown: function() {
      if (!PERSIST_DATABASES) {
        Pouch.destroy(this.local);
        Pouch.destroy(this.remote);
      }
    }
  });

  var doc = { _id: '0', integer: 0 };

  asyncTest('Testing issue #221', function() {
    var self = this;
    // Feature detection for pouchdb-server
    ajax({ url: 'http://localhost:2020/' }, function (err, ret) {
      if (!ret.couchdb) {
        ok(true, 'Test not applicable to backend.');
        return start();
      }
      // Create databases.
      initDBPair(self.local, self.remote, function(local, remote) {
        // Write a doc in CouchDB.
        remote.put(doc, function(err, results) {
          // Update the doc.
          doc._rev = results.rev;
          doc.integer = 1;
          remote.put(doc, function(err, results) {
            // Compact the db.
            remote.compact(function() {
              remote.get(doc._id, {revs_info:true},function(err, data) {
                var correctRev = data._revs_info[0];
                local.replicate.from(remote, function(err, results) {
                  // Check the PouchDB doc.
                  local.get(doc._id, function(err, results) {
                    strictEqual(results._rev, correctRev.rev,
                       'correct rev stored after replication');
                    strictEqual(results.integer, 1,
                       'correct content stored after replication');
                    start();
                  });
                });
              });
            });
          });
        });
      });
    });
  });

  asyncTest('Testing issue #221 again', function() {
    var self = this;
    // Feature detection for pouchdb-server
    ajax({ url: 'http://localhost:2020/' }, function (err, ret) {
      if (!ret.couchdb) {
        ok(true, 'Test not applicable to backend.');
        return start();
      }
      // Create databases.
      initDBPair(self.local, self.remote, function(local, remote) {
        // Write a doc in CouchDB.
        remote.put(doc, function(err, results) {
          doc._rev = results.rev;
          // Second doc so we get 2 revisions from replicate.
          remote.put(doc, function(err, results) {
            doc._rev = results.rev;
            local.replicate.from(remote, function(err, results) {
              doc.integer = 1;
              // One more change
              remote.put(doc, function(err, results) {
                // Testing if second replications fails now
                local.replicate.from(remote, function(err, results) {
                  local.get(doc._id, function(err, results) {
                    strictEqual(results.integer, 1, 'correct content stored after replication');
                    start();
                  });
                });
              });
            });
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>var config = require('../lib/config');

var helperServer = require('./helperServer');
var helperQuestion = require('./helperQuestion');

const locals = {};

locals.siteUrl = 'http://localhost:' + config.serverPort;
locals.baseUrl = locals.siteUrl + '/pl';
locals.courseInstanceBaseUrl = locals.baseUrl + '/course_instance/1/instructor';
locals.questionBaseUrl = locals.courseInstanceBaseUrl + '/question';
locals.questionsUrl = locals.courseInstanceBaseUrl + '/questions';
locals.isStudentPage = false;

const qids = [
    'addNumbers',
    'ballToss2',
    'customElement',
    'differentiatePolynomial',
    'downloadFile',
    // FIXME: 'fibonacciUpload',
    'functionValueFromPlot',
    'multiplyTwoMatrices',
    'multiplyTwoNumbers',
    'positionTimeGraph',
];

describe('Auto-test questions', function() {
    this.timeout(20000);

    before('set up testing server', helperServer.before);
    after('shut down testing server', helperServer.after);

    qids.forEach(qid => helperQuestion.autoTestQuestion(locals, qid));
});
<commit_msg>add more questions to test<commit_after>var config = require('../lib/config');

var helperServer = require('./helperServer');
var helperQuestion = require('./helperQuestion');

const locals = {};

locals.siteUrl = 'http://localhost:' + config.serverPort;
locals.baseUrl = locals.siteUrl + '/pl';
locals.courseInstanceBaseUrl = locals.baseUrl + '/course_instance/1/instructor';
locals.questionBaseUrl = locals.courseInstanceBaseUrl + '/question';
locals.questionsUrl = locals.courseInstanceBaseUrl + '/questions';
locals.isStudentPage = false;

const qids = [
    'addNumbers',
    'ballToss2',
    'customElement',
    'differentiatePolynomial',
    'downloadFile',
    // FIXME: 'fibonacciUpload',
    'functionValueFromPlot',
    'multiplyTwoMatrices',
    'multiplyTwoNumbers',
    'positionTimeGraph',
    'addBinary',
    'addComplexNumbers',
    'codeHighlight',
    'multiplyTwoComplexMatrices',
    // FIXME: 'rotateObject',
];

describe('Auto-test questions', function() {
    this.timeout(20000);

    before('set up testing server', helperServer.before);
    after('shut down testing server', helperServer.after);

    qids.forEach(qid => helperQuestion.autoTestQuestion(locals, qid));
});
<|endoftext|>


<commit_before>var BOARD_HEIGHT = 288;
var BOARD_WIDTH = 224;
var VERT_TILES = BOARD_HEIGHT / 8;
var HORIZ_TILES = BOARD_WIDTH / 8;

gameBoard = new Array(VERT_TILES);
for(var y = 0; y < VERT_TILES; y++) {
  gameBoard[y] = new Array(HORIZ_TILES);
  for(var x = 0; x < HORIZ_TILES; x++) {
    gameBoard[y][x] = "";
  }
}

var canvas, context, canvasData;

var drawPixel = function(args) {
  var index = (args.x + args.y * canvas.width) * 4;

  canvasData.data[index + 0] = args.r;
  canvasData.data[index + 1] = args.g;
  canvasData.data[index + 2] = args.b;
  canvasData.data[index + 3] = args.a;
}

var drawObject = function(args) {
  var width = args.objectArr.length;
  var height = args.objectArr[0].length;
  context.clearRect(args.x, args.y, width, height);

  args.objectArr.forEach(function(row, rIndex) {
    row.forEach(function(col, cIndex) {
      if(col == "x") {
        drawPixel({
          x: args.x + cIndex,
          y: args.y + rIndex,
          r: args.color.r,
          g: args.color.g,
          b: args.color.b,
          a: args.color.a
        });
      }
    });
  });
}

var drawPac = function(x, y) {
  context.fillStyle = "#FF0";
  context.beginPath();
  context.arc(x, y, 6, 0.25 * Math.PI, 1.75 * Math.PI, false);
  context.lineTo(x, y);
  context.closePath();
  context.fill();
}
var drawGhost = function(xPos, yPos, color) {
  context.fillStyle = color;
  var x = xPos;
  var y = yPos;

  // body
  context.beginPath();
  context.arc(x, y, 6, Math.PI, 0, false);
  context.moveTo(x += 7, y);
  context.lineTo(x, y += 9);
  context.lineTo(x -= 1, y);
  context.lineTo(x -= 2, y -= 2.5);
  context.lineTo(x -= 2, y += 2.5);
  context.lineTo(x -= 1, y);
  context.lineTo(x, y -= 2);
  context.lineTo(x -= 2, y);
  context.lineTo(x, y += 2);
  context.lineTo(x -= 1, y);
  context.lineTo(x -= 2, y -= 2.5);
  context.lineTo(x -= 2, y += 2.5);
  context.lineTo(x -= 1, y);
  context.lineTo(x, y -= 9);
  context.closePath();
  context.fill();
}

var aCxt, pacOsc, pacGain, pacModOsc, pacModGain;

var initAudio = function() {
  aCxt = !!AudioContext ? new AudioContext() : new webkitAudioContext();
  pacOsc = aCxt.createOscillator();
  pacModOsc = aCxt.createOscillator();
  pacGain = aCxt.createGain();
  pacModGain = aCxt.createGain();

  pacOsc.connect(pacGain);
  pacGain.connect(aCxt.destination);
  pacModOsc.connect(pacModGain);
  pacModGain.connect(pacOsc.frequency);

  pacModOsc.type = "triangle";
  pacOsc.type = "triangle";
  pacOsc.frequency.value = 415;
  pacGain.gain.value = 0.1;
  pacModOsc.frequency.value = 2.67;
  pacModGain.gain.value = 100;
}

var startPacAudio = function(seconds) {
  var runSeconds = seconds || 2;
  pacOsc.start();
  pacModOsc.start();
  pacOsc.stop(runSeconds);
  pacModOsc.stop(runSeconds);
}

var ready = function(fun) {
  if(document.readyState != "loading") {
    fun();
  }
  else if(document.addEventListener) {
    document.addEventListener("DOMContentLoaded", fun);
  }
  else {
    document.attachEvent("onreadystatechange", function() {
      if(document.readyState != "loading") {
        fun();
      }
    });
  }
}

ready(function() {
  canvas = document.getElementById("board");
  context = canvas.getContext("2d");
  canvasData = context.getImageData(0, 0, canvas.width, canvas.height);

  var debugDisp = document.getElementById("debug");

  var x = 8;
  var y = 8;
  for(var prop in charset) {
    drawObject({
      x: x,
      y: y,
      objectArr: charset[prop],
      color: {r: 255, g: 255, b: 255, a: 255}
    });
    x += 8;
    if(x + 8 > BOARD_WIDTH) {
      x = 8;
      y += 8;
    }
  }

  context.putImageData(canvasData, 0, 0);

  drawBorders();
  drawPac(113, 212);
  drawGhost(112, 115, "#F00");

  // context.fillStyle = "#080";
  // for(var x = 0; x < BOARD_WIDTH; x += 8) {
  //   context.fillRect(x, 0, 1, BOARD_HEIGHT);
  // }
  // for(var y = 0; y < BOARD_HEIGHT; y += 8) {
  //   context.fillRect(0, y, BOARD_WIDTH, 1);
  // }
});
<commit_msg>Add other three ghosts for reference<commit_after>var BOARD_HEIGHT = 288;
var BOARD_WIDTH = 224;
var VERT_TILES = BOARD_HEIGHT / 8;
var HORIZ_TILES = BOARD_WIDTH / 8;

gameBoard = new Array(VERT_TILES);
for(var y = 0; y < VERT_TILES; y++) {
  gameBoard[y] = new Array(HORIZ_TILES);
  for(var x = 0; x < HORIZ_TILES; x++) {
    gameBoard[y][x] = "";
  }
}

var canvas, context, canvasData;

var drawPixel = function(args) {
  var index = (args.x + args.y * canvas.width) * 4;

  canvasData.data[index + 0] = args.r;
  canvasData.data[index + 1] = args.g;
  canvasData.data[index + 2] = args.b;
  canvasData.data[index + 3] = args.a;
}

var drawObject = function(args) {
  var width = args.objectArr.length;
  var height = args.objectArr[0].length;
  context.clearRect(args.x, args.y, width, height);

  args.objectArr.forEach(function(row, rIndex) {
    row.forEach(function(col, cIndex) {
      if(col == "x") {
        drawPixel({
          x: args.x + cIndex,
          y: args.y + rIndex,
          r: args.color.r,
          g: args.color.g,
          b: args.color.b,
          a: args.color.a
        });
      }
    });
  });
}

var drawPac = function(x, y) {
  context.fillStyle = "#FF0";
  context.beginPath();
  context.arc(x, y, 6, 0.25 * Math.PI, 1.75 * Math.PI, false);
  context.lineTo(x, y);
  context.closePath();
  context.fill();
}
var drawGhost = function(xPos, yPos, color) {
  context.fillStyle = color;
  var x = xPos;
  var y = yPos;

  // body
  context.beginPath();
  context.arc(x, y, 6, Math.PI, 0, false);
  context.moveTo(x += 7, y);
  context.lineTo(x, y += 9);
  context.lineTo(x -= 1, y);
  context.lineTo(x -= 2, y -= 2.5);
  context.lineTo(x -= 2, y += 2.5);
  context.lineTo(x -= 1, y);
  context.lineTo(x, y -= 2);
  context.lineTo(x -= 2, y);
  context.lineTo(x, y += 2);
  context.lineTo(x -= 1, y);
  context.lineTo(x -= 2, y -= 2.5);
  context.lineTo(x -= 2, y += 2.5);
  context.lineTo(x -= 1, y);
  context.lineTo(x, y -= 9);
  context.closePath();
  context.fill();
}
var aCxt, pacOsc, pacGain, pacModOsc, pacModGain;

var initAudio = function() {
  aCxt = !!AudioContext ? new AudioContext() : new webkitAudioContext();
  pacOsc = aCxt.createOscillator();
  pacModOsc = aCxt.createOscillator();
  pacGain = aCxt.createGain();
  pacModGain = aCxt.createGain();

  pacOsc.connect(pacGain);
  pacGain.connect(aCxt.destination);
  pacModOsc.connect(pacModGain);
  pacModGain.connect(pacOsc.frequency);

  pacModOsc.type = "triangle";
  pacOsc.type = "triangle";
  pacOsc.frequency.value = 415;
  pacGain.gain.value = 0.1;
  pacModOsc.frequency.value = 2.67;
  pacModGain.gain.value = 100;
}

var startPacAudio = function(seconds) {
  var runSeconds = seconds || 2;
  pacOsc.start();
  pacModOsc.start();
  pacOsc.stop(runSeconds);
  pacModOsc.stop(runSeconds);
}

var ready = function(fun) {
  if(document.readyState != "loading") {
    fun();
  }
  else if(document.addEventListener) {
    document.addEventListener("DOMContentLoaded", fun);
  }
  else {
    document.attachEvent("onreadystatechange", function() {
      if(document.readyState != "loading") {
        fun();
      }
    });
  }
}

ready(function() {
  canvas = document.getElementById("board");
  context = canvas.getContext("2d");
  canvasData = context.getImageData(0, 0, canvas.width, canvas.height);

  var debugDisp = document.getElementById("debug");

  var x = 8;
  var y = 8;
  for(var prop in charset) {
    drawObject({
      x: x,
      y: y,
      objectArr: charset[prop],
      color: {r: 255, g: 255, b: 255, a: 255}
    });
    x += 8;
    if(x + 8 > BOARD_WIDTH) {
      x = 8;
      y += 8;
    }
  }

  context.putImageData(canvasData, 0, 0);

  drawBorders();
  drawPac(113, 212);
  drawGhost(112, 115, "#F00");
  drawGhost(96, 139, "#4FF");
  drawGhost(112, 139, "#FCF");
  drawGhost(128, 139, "#FA6");

  // context.fillStyle = "#080";
  // for(var x = 0; x < BOARD_WIDTH; x += 8) {
  //   context.fillRect(x, 0, 1, BOARD_HEIGHT);
  // }
  // for(var y = 0; y < BOARD_HEIGHT; y += 8) {
  //   context.fillRect(0, y, BOARD_WIDTH, 1);
  // }
});
<|endoftext|>