<commit_before>define(function (require) {
    'use strict';

    var $ = require('jquery'),

        CourseMetadataModel = require('learners/common/models/course-metadata'),
        LearnerCollection = require('learners/common/collections/learners'),
        LearnersController = require('learners/app/controller'),
        LearnersRootView = require('learners/app/views/root'),
        PageModel = require('learners/common/models/page'),
        TrackingModel = require('models/tracking-model');

    describe('LearnersController', function () {
        var courseId,
            expectDetailPage,
            expectRosterPage,
            server;

        courseId = 'test/course/id';

        // convenience method for asserting that we are on the learner detail page
        expectDetailPage = function (controller) {
            var date = new Date(2016, 1, 28);
            expect(controller.options.rootView.$('.learners-navigation-region').html())
                .toContainText('Return to Learners');
            expect(controller.options.rootView.$('.learner-detail-container')).toExist();
            expect(controller.options.rootView.$('.learner-summary-container')).toExist();
            expect(controller.options.rootView.$('.learners-header-region').html())
                .toContainText('Learner Details');
            expect(controller.options.rootView.$('.learners-header-region').html())
                .not.toContainText(date.toLocaleDateString('en-us', {year: 'numeric', month: 'long', day: 'numeric'}));
            expect(controller.options.trackingModel.get('page')).toBe('learner_details');
            expect(controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
        };

        // convenience method for asserting that we are on the roster page
        expectRosterPage = function (controller) {
            expect(controller.options.rootView.$('.learners-navigation-region').html())
                .not.toContainText('Return to Learners');
            expect(controller.options.rootView.$('.learner-roster')).toBeInDOM();
            expect(controller.options.rootView.$('.learners-header-region').html()).toContainText('Learners');
            expect(controller.options.trackingModel.get('page')).toBe('learner_roster');
            expect(controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
        };

        beforeEach(function () {
            var pageModel = new PageModel();

            server = sinon.fakeServer.create();
            setFixtures('

');
            this.rootView = new LearnersRootView({
                el: '.root-view',
                pageModel: pageModel
            });
            this.rootView.render();
            // The learner roster view looks at the first learner in
            // the collection in order to render a last updated
            // message.
            this.user = {
                name: 'learner',
                username: 'learner',
                email: 'learner@example.com',
                account_url: 'example.com/learner',
                enrollment_mode: 'audit',
                enrollment_date: new Date(),
                cohort: null,
                segments: ['highly_engaged'],
                engagements: {},
                last_updated: new Date(2016, 1, 28),
                course_id: courseId
            };
            this.collection = new LearnerCollection([this.user], {parse: true, url:'http://example.com'});
            this.controller = new LearnersController({
                rootView: this.rootView,
                learnerCollection: this.collection,
                courseMetadata: new CourseMetadataModel(),
                pageModel: pageModel,
                learnerEngagementTimelineUrl: '/test-engagement-endpoint/',
                learnerListUrl: '/test-learner-endpoint/',
                courseId: courseId,
                trackingModel: new TrackingModel()
            });
            spyOn(this.controller.options.trackingModel, 'trigger');
        });

        afterEach(function () {
            server.restore();
        });

        it('should show the learner roster page', function () {
            this.controller.showLearnerRosterPage();
            expectRosterPage(this.controller);
        });

        it('should show the filtered learner roster page', function (done) {
            this.controller.showLearnerRosterPage('text_search=foo');
            this.controller.options.learnerCollection.once('sync', function () {
                expectRosterPage(this.controller);
                expect(this.controller.options.rootView.$('.learners-active-filters').html()).toContainText('foo');
                done();
            }, this, done);
            expect(this.controller.options.rootView.$('.learners-main-region').html()).toContainText('Loading...');
            server.requests[server.requests.length - 1].respond(200, {}, '{}');
        });

        it('should show invalid parameters alert with invalid URL parameters', function () {
            this.controller.showLearnerRosterPage('text_search=foo=');
            expect(this.controller.options.rootView.$('.learners-alert-region').html()).toContainText(
                'Invalid Parameters'
            );
            expect(this.controller.options.rootView.$('.learners-main-region').html()).toBe('');
        });

        describe('navigating to the Learner Detail page', function () {
            it('should show the learner detail page', function () {
                var engagementTimelineResponse;
                this.controller.showLearnerDetailPage('learner');
                // Showing the learner detail page triggers a request for the
                // learner engagement timeline data.
                engagementTimelineResponse = JSON.stringify({days: [{
                    date: '2016-01-01',
                    discussion_contributions: 1,
                    problems_attempted: 1,
                    problems_completed: 1,
                    videos_viewed: 1
                }]});
                server.requests[0].respond(200, {}, JSON.stringify(this.user));
                server.requests[server.requests.length - 1].respond(200, {}, engagementTimelineResponse);
                expectDetailPage(this.controller);
            });

            it('should handle AJAX errors', function (done) {
                var view = this.controller.showLearnerDetailPage('example-username');

                view.once('appError', function (options) {
                    expect(options.title).toBe('Server error');
                    done();
                });

                server.requests[server.requests.length - 1].respond(500, {}, '');
            });
        });

        // The 'showPage' event gets fired by the router on the
        // controller any time a route is hit which should change the
        // current page.
        describe('showPage event', function () {
            it('renders the loading bar', function () {
                jasmine.clock().install();
                expect($('#nprogress')).not.toExist();
                this.controller.triggerMethod('showPage');
                expect($('#nprogress')).toExist();
                jasmine.clock().uninstall();
            });

            it('hides app-wide errors', function () {
                this.controller.showLearnerDetailPage('learner');
                server.requests[0].respond(200, {}, JSON.stringify(this.user));
                server.requests[server.requests.length - 1].respond(500, {});
                expectDetailPage(this.controller);
                expect(this.rootView.$('[role="alert"]')).toExist();
                this.controller.triggerMethod('showPage');
                expect(this.rootView.$('[role="alert"]')).not.toExist();
            });
        });

        it('should show the not found page', function () {
            this.controller.showNotFoundPage();
            expect(this.rootView.$el.html()).toContainText("Sorry, we couldn't find the page you're looking for.");  // jshint ignore:line
            expect(this.controller.options.trackingModel.get('page')).toBe('learner_not_found');
            expect(this.controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
        });
    });
});
<commit_msg>Test for learner detail for navigation deep link<commit_after>define(function (require) {
    'use strict';

    var $ = require('jquery'),

        CourseMetadataModel = require('learners/common/models/course-metadata'),
        LearnerCollection = require('learners/common/collections/learners'),
        LearnersController = require('learners/app/controller'),
        LearnersRootView = require('learners/app/views/root'),
        PageModel = require('learners/common/models/page'),
        TrackingModel = require('models/tracking-model');

    describe('LearnersController', function () {
        var courseId,
            expectDetailPage,
            expectRosterPage,
            server;

        courseId = 'test/course/id';

        // convenience method for asserting that we are on the learner detail page
        expectDetailPage = function (controller) {
            var date = new Date(2016, 1, 28);
            expect(controller.options.rootView.$('.learners-navigation-region').html())
                .toContainText('Return to Learners');
            expect(controller.options.rootView.$('.learner-detail-container')).toExist();
            expect(controller.options.rootView.$('.learner-summary-container')).toExist();
            expect(controller.options.rootView.$('.learners-header-region').html())
                .toContainText('Learner Details');
            expect(controller.options.rootView.$('.learners-header-region').html())
                .not.toContainText(date.toLocaleDateString('en-us', {year: 'numeric', month: 'long', day: 'numeric'}));
            expect(controller.options.trackingModel.get('page')).toBe('learner_details');
            expect(controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
        };

        // convenience method for asserting that we are on the roster page
        expectRosterPage = function (controller) {
            expect(controller.options.rootView.$('.learners-navigation-region').html())
                .not.toContainText('Return to Learners');
            expect(controller.options.rootView.$('.learner-roster')).toBeInDOM();
            expect(controller.options.rootView.$('.learners-header-region').html()).toContainText('Learners');
            expect(controller.options.trackingModel.get('page')).toBe('learner_roster');
            expect(controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
        };

        beforeEach(function () {
            var pageModel = new PageModel();

            server = sinon.fakeServer.create();
            setFixtures('

');
            this.rootView = new LearnersRootView({
                el: '.root-view',
                pageModel: pageModel
            });
            this.rootView.render();
            // The learner roster view looks at the first learner in
            // the collection in order to render a last updated
            // message.
            this.user = {
                name: 'learner',
                username: 'learner',
                email: 'learner@example.com',
                account_url: 'example.com/learner',
                enrollment_mode: 'audit',
                enrollment_date: new Date(),
                cohort: null,
                segments: ['highly_engaged'],
                engagements: {},
                last_updated: new Date(2016, 1, 28),
                course_id: courseId
            };
            this.collection = new LearnerCollection([this.user], {parse: true, url:'http://example.com'});
            this.controller = new LearnersController({
                rootView: this.rootView,
                learnerCollection: this.collection,
                courseMetadata: new CourseMetadataModel(),
                pageModel: pageModel,
                learnerEngagementTimelineUrl: '/test-engagement-endpoint/',
                learnerListUrl: '/test-learner-endpoint/',
                courseId: courseId,
                trackingModel: new TrackingModel()
            });
            spyOn(this.controller.options.trackingModel, 'trigger');
        });

        afterEach(function () {
            server.restore();
        });

        it('should show the learner roster page', function () {
            this.controller.showLearnerRosterPage();
            expectRosterPage(this.controller);
        });

        it('should show the filtered learner roster page', function (done) {
            this.controller.showLearnerRosterPage('text_search=foo');
            this.controller.options.learnerCollection.once('sync', function () {
                expectRosterPage(this.controller);
                expect(this.controller.options.rootView.$('.learners-active-filters').html()).toContainText('foo');
                done();
            }, this, done);
            expect(this.controller.options.rootView.$('.learners-main-region').html()).toContainText('Loading...');
            server.requests[server.requests.length - 1].respond(200, {}, '{}');
        });

        it('should show invalid parameters alert with invalid URL parameters', function () {
            this.controller.showLearnerRosterPage('text_search=foo=');
            expect(this.controller.options.rootView.$('.learners-alert-region').html()).toContainText(
                'Invalid Parameters'
            );
            expect(this.controller.options.rootView.$('.learners-main-region').html()).toBe('');
        });

        describe('navigating to the Learner Detail page', function () {
            it('should show the learner detail page', function () {
                var engagementTimelineResponse;
                this.controller.showLearnerDetailPage('learner');
                // Showing the learner detail page triggers a request for the
                // learner engagement timeline data.
                engagementTimelineResponse = JSON.stringify({days: [{
                    date: '2016-01-01',
                    discussion_contributions: 1,
                    problems_attempted: 1,
                    problems_completed: 1,
                    videos_viewed: 1
                }]});
                server.requests[0].respond(200, {}, JSON.stringify(this.user));
                server.requests[server.requests.length - 1].respond(200, {}, engagementTimelineResponse);
                expectDetailPage(this.controller);
            });

            it('should handle AJAX errors', function (done) {
                var view = this.controller.showLearnerDetailPage('example-username');

                view.once('appError', function (options) {
                    expect(options.title).toBe('Server error');
                    done();
                });

                server.requests[server.requests.length - 1].respond(500, {}, '');
            });

            it('should have query string in return to learners navigation link', function () {
                this.collection.state.currentPage = 2;
                this.collection.setSearchString('foobar');
                this.controller.showLearnerDetailPage('learner');
                server.requests[0].respond(200, {}, JSON.stringify(this.user));
                server.requests[server.requests.length - 1].respond(200, {}, JSON.stringify({}));
                expect(this.controller.options.rootView.$('.learners-navigation-region a').attr('href'))
                    .toEqual('#?text_search=foobar&page=2');
                });
        });

        // The 'showPage' event gets fired by the router on the
        // controller any time a route is hit which should change the
        // current page.
        describe('showPage event', function () {
            it('renders the loading bar', function () {
                jasmine.clock().install();
                expect($('#nprogress')).not.toExist();
                this.controller.triggerMethod('showPage');
                expect($('#nprogress')).toExist();
                jasmine.clock().uninstall();
            });

            it('hides app-wide errors', function () {
                this.controller.showLearnerDetailPage('learner');
                server.requests[0].respond(200, {}, JSON.stringify(this.user));
                server.requests[server.requests.length - 1].respond(500, {});
                expectDetailPage(this.controller);
                expect(this.rootView.$('[role="alert"]')).toExist();
                this.controller.triggerMethod('showPage');
                expect(this.rootView.$('[role="alert"]')).not.toExist();
            });
        });

        it('should show the not found page', function () {
            this.controller.showNotFoundPage();
            expect(this.rootView.$el.html()).toContainText("Sorry, we couldn't find the page you're looking for.");  // jshint ignore:line
            expect(this.controller.options.trackingModel.get('page')).toBe('learner_not_found');
            expect(this.controller.options.trackingModel.trigger).toHaveBeenCalledWith('segment:page');
        });
    });
});
<|endoftext|>


<commit_before>/*
  backgrid-text-cell
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong
  Licensed under the MIT @license.
*/

(function (window, $, _, Backbone, Backgrid)  {

  /**
     Renders a form with a text area and a save button in a modal dialog.

     @class Backgrid.Extension.TextareaEditor
     @extends Backgrid.InputCellEditor
   */
  var TextareaEditor = Backgrid.Extension.TextareaEditor = Backgrid.CellEditor.extend({

    /** @property */
    tagName: "div",

    /** @property */
    className: "modal hide fade",

    /** @property {function(Object, ?Object=): string} template */
    template: _.template('<form>
<button type="button" class="close" data-dismiss="modal">×</button>
<%- column.get("label") %>

<textarea cols="<%= cols %>" rows="<%= rows %>"><%- content %></textarea>

<input class="btn" type="submit" value="Save"/>
</form>'),

    /** @property */
    cols: 80,

    /** @property */
    rows: 10,

    /** @property */
    events: {
      "submit": "save",
      "hide": "cancel",
      "hidden": "close"
    },

    /**
       @property {Object} modalOptions The options passed to Bootstrap's modal
       plugin.
    */
    modalOptions: {
      backdrop: false
    },

    /**
       Renders a modal form dialog with a textarea, submit button and a close button.
    */
    render: function () {
      this.$el.html($(this.template({
        column: this.column,
        cols: this.cols,
        rows: this.rows,
        content: this.formatter.fromRaw(this.model.get(this.column.get("name")))
      })));

      this.$el.modal(this.modalOptions);

      return this;
    },

    /**
       Event handler. Saves the text in the text area to the model.

       Triggers a Backbone `error` event if the value cannot be
       converted. Classes listening to the `error` event, usually the Cell
       classes, should respond appropriately, usually by rendering some kind of
       error feedback.

       @param {Event} e
    */
    save: function (e) {
      if (e) e.preventDefault();

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      if (_.isUndefined(content) ||
          !this.model.set(this.column.get("name"), content, {validate: true})) {
        this.trigger("error");
      }
      else {
        this.$el.modal("hide");
      }
    },

    /**
       Event handler. Revert the text in the model after asking for confirmation
       if dirty, otherwise just close the editor.

       @param {Event} e
    */
    cancel: function (e) {

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      // Dirty
      if (content !== this.model.get(this.column.get("name")).replace(/\r/g, '') &&
          window.confirm("Would you like to save your changes?")) {
        e.preventDefault();
        e.stopPropagation();
        return this.save();
      }
    },

    /**
       Triggers a `done` event after the modal is hidden.
     */
    close: function () {
      this.trigger("done");
    }

  });

  /**
     TextCell is a string cell type that renders a form with a text area in a
     modal dialog instead of an input box editor. It is best suited for entering
     a large body of text.

     @class Backgrid.Extension.TextCell
     @extends Backgrid.StringCell
  */
  var TextCell = Backgrid.Extension.TextCell = Backgrid.StringCell.extend({

    /** @property */
    className: "text-cell",

    /** @property  */
    editor: TextareaEditor

  });

}(window, jQuery, _, Backbone, Backgrid));
<commit_msg>Fix TextareaEditor extends jsdoc<commit_after>/*
  backgrid-text-cell
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong
  Licensed under the MIT @license.
*/

(function (window, $, _, Backbone, Backgrid)  {

  /**
     Renders a form with a text area and a save button in a modal dialog.

     @class Backgrid.Extension.TextareaEditor
     @extends Backgrid.CellEditor
   */
  var TextareaEditor = Backgrid.Extension.TextareaEditor = Backgrid.CellEditor.extend({

    /** @property */
    tagName: "div",

    /** @property */
    className: "modal hide fade",

    /** @property {function(Object, ?Object=): string} template */
    template: _.template('<form>
<button type="button" class="close" data-dismiss="modal">×</button>
<%- column.get("label") %>

<textarea cols="<%= cols %>" rows="<%= rows %>"><%- content %></textarea>

<input class="btn" type="submit" value="Save"/>
</form>'),

    /** @property */
    cols: 80,

    /** @property */
    rows: 10,

    /** @property */
    events: {
      "submit": "save",
      "hide": "cancel",
      "hidden": "close"
    },

    /**
       @property {Object} modalOptions The options passed to Bootstrap's modal
       plugin.
    */
    modalOptions: {
      backdrop: false
    },

    /**
       Renders a modal form dialog with a textarea, submit button and a close button.
    */
    render: function () {
      this.$el.html($(this.template({
        column: this.column,
        cols: this.cols,
        rows: this.rows,
        content: this.formatter.fromRaw(this.model.get(this.column.get("name")))
      })));

      this.$el.modal(this.modalOptions);

      return this;
    },

    /**
       Event handler. Saves the text in the text area to the model.

       Triggers a Backbone `error` event if the value cannot be
       converted. Classes listening to the `error` event, usually the Cell
       classes, should respond appropriately, usually by rendering some kind of
       error feedback.

       @param {Event} e
    */
    save: function (e) {
      if (e) e.preventDefault();

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      if (_.isUndefined(content) ||
          !this.model.set(this.column.get("name"), content, {validate: true})) {
        this.trigger("error");
      }
      else {
        this.$el.modal("hide");
      }
    },

    /**
       Event handler. Revert the text in the model after asking for confirmation
       if dirty, otherwise just close the editor.

       @param {Event} e
    */
    cancel: function (e) {

      var content = this.formatter.toRaw(this.$el.find("textarea").val());

      // Dirty
      if (content !== this.model.get(this.column.get("name")).replace(/\r/g, '') &&
          window.confirm("Would you like to save your changes?")) {
        e.preventDefault();
        e.stopPropagation();
        return this.save();
      }
    },

    /**
       Triggers a `done` event after the modal is hidden.
     */
    close: function () {
      this.trigger("done");
    }

  });

  /**
     TextCell is a string cell type that renders a form with a text area in a
     modal dialog instead of an input box editor. It is best suited for entering
     a large body of text.

     @class Backgrid.Extension.TextCell
     @extends Backgrid.StringCell
  */
  var TextCell = Backgrid.Extension.TextCell = Backgrid.StringCell.extend({

    /** @property */
    className: "text-cell",

    /** @property  */
    editor: TextareaEditor

  });

}(window, jQuery, _, Backbone, Backgrid));
<|endoftext|>


<commit_before>
/**
 * @license
 * Copyright 2017 The FOAM Authors. 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.
 */

foam.CLASS({
  package: 'foam.box.pipeline',
  name: 'PipelineManager',

  requires: [
    'foam.box.Box',
    'foam.box.BroadcastBox',
    'foam.box.Message',
    'foam.box.RPCMessage',
    'foam.box.RPCReturnBox',
    'foam.box.pipeline.Pipeline'
  ],
  imports: [
    'registry'
  ],

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.box.pipeline.Pipeline',
      name: 'pipeline'
    },
    {
      class: 'FObjectArray',
      of: 'foam.box.pipeline.PipelineManager',
      name: 'delegates',
      postSet: function(old, nu) {
        var pl = this.pipeline;

        // Get non-RPCMessage-wrapped box. This is the output's delegate if
        // wrapping occurred before (i.e. old value was non-empty), otheriwse,
        // it's just the output.
        var plRemoteBox = old.length > 0 ? pl.output.delegate : pl.output;

        if ( nu.length === 0 ) {
          // End of the line. Do not wrap RPCMessage around output.
          pl.output = pl.defaultOutput;
        } else if ( this.delegates.length === 1 ) {
          // Just one delegate. Wrap RPCMessage around output value.
          pl.output = nu[0].remoteInput;
        } else {
          // Many delegates. Wrap RPCMessage around output value and pass along
          // to all delegates.
          this.output = this.BroadcastBox.create({
            delegates: nu.map(function(pipelineBuilder) {
              return pipelineBuilder.remoteInput;
            })
          });
        }
      }
    }
  ],

  methods: [
    function init() {
      this.validate();
      foam.assert(this.Box.isInstance(this.registry),
                  'Pipeline requires registry that implements Box');
      this.SUPER();
    },
    function then(runnable) {
      var pl = this.pipeline;
      if ( ! pl.runnable ) {
        pl.runnable = runnable;
        return this;
      }

      var next = this.clone();
      var nextPL = next.pipeline = this.Pipeline.create();
      nextPL.runnable = runnable;
      this.delegates = this.delegates.concat([next]);
      return next;
    },
    function all() {
      var ret = [];
      for ( var i = 0; i < arguments.length; i++ ) {
        ret.push(this.then(arguments[i]));
      }
      return ret;
    },
    function build() {
      var pl = this.pipeline;
      //var onRegisteredBox = ;
      var onRegisteredPromise = onRegisteredBox.promise;
      this.registry.send(this.Message.create({
        object: this.RPCMessage.create({
          name: 'register',
          args: [ null, null, pl.localInput ]
        }),
        attributes: {
          replyBox: onRegisteredBox.RPCReturnBox.create(),
          errorBox: pl.errorBox
        }
      }));
      pl.remoteInput.delegate = onRegisteredPromise;

      return pl.remoteInput;
    }
  ]
});
<commit_msg>Rename the model that lives in PipelineManagerNonRPC.js to PipelineManagerNonRPC.<commit_after>
/**
 * @license
 * Copyright 2017 The FOAM Authors. 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.
 */

foam.CLASS({
  package: 'foam.box.pipeline',
  name: 'PipelineManagerNonRPC',

  requires: [
    'foam.box.Box',
    'foam.box.BroadcastBox',
    'foam.box.Message',
    'foam.box.RPCMessage',
    'foam.box.RPCReturnBox',
    'foam.box.pipeline.Pipeline'
  ],
  imports: [
    'registry'
  ],

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.box.pipeline.Pipeline',
      name: 'pipeline'
    },
    {
      class: 'FObjectArray',
      of: 'foam.box.pipeline.PipelineManagerNonRPC',
      name: 'delegates',
      postSet: function(old, nu) {
        var pl = this.pipeline;

        // Get non-RPCMessage-wrapped box. This is the output's delegate if
        // wrapping occurred before (i.e. old value was non-empty), otheriwse,
        // it's just the output.
        var plRemoteBox = old.length > 0 ? pl.output.delegate : pl.output;

        if ( nu.length === 0 ) {
          // End of the line. Do not wrap RPCMessage around output.
          pl.output = pl.defaultOutput;
        } else if ( this.delegates.length === 1 ) {
          // Just one delegate. Wrap RPCMessage around output value.
          pl.output = nu[0].remoteInput;
        } else {
          // Many delegates. Wrap RPCMessage around output value and pass along
          // to all delegates.
          this.output = this.BroadcastBox.create({
            delegates: nu.map(function(pipelineBuilder) {
              return pipelineBuilder.remoteInput;
            })
          });
        }
      }
    }
  ],

  methods: [
    function init() {
      this.validate();
      foam.assert(this.Box.isInstance(this.registry),
                  'Pipeline requires registry that implements Box');
      this.SUPER();
    },
    function then(runnable) {
      var pl = this.pipeline;
      if ( ! pl.runnable ) {
        pl.runnable = runnable;
        return this;
      }

      var next = this.clone();
      var nextPL = next.pipeline = this.Pipeline.create();
      nextPL.runnable = runnable;
      this.delegates = this.delegates.concat([next]);
      return next;
    },
    function all() {
      var ret = [];
      for ( var i = 0; i < arguments.length; i++ ) {
        ret.push(this.then(arguments[i]));
      }
      return ret;
    },
    function build() {
      var pl = this.pipeline;
      //var onRegisteredBox = ;
      var onRegisteredPromise = onRegisteredBox.promise;
      this.registry.send(this.Message.create({
        object: this.RPCMessage.create({
          name: 'register',
          args: [ null, null, pl.localInput ]
        }),
        attributes: {
          replyBox: onRegisteredBox.RPCReturnBox.create(),
          errorBox: pl.errorBox
        }
      }));
      pl.remoteInput.delegate = onRegisteredPromise;

      return pl.remoteInput;
    }
  ]
});
<|endoftext|>


<commit_before>import React from 'react';
import {BernieText, BernieColors} from '../styles/bernie-css'
import GCFormField from './GCFormField';
import Select from 'react-select';
require('react-select/dist/react-select.css');

export default class GCSelectField extends GCFormField {
  styles = {
    label: {
      paddingBottom: 10,
      ...BernieText.inputLabel,
    },
  }

  createMenuItems() {
    return Object.keys(this.props.choices).map((choice) => {
      return {
        label: this.props.choices[choice],
        value: choice
      }
    })
  }

  getMenuItemIndex(menuItems) {
    let menuItemIndex = 0;
    menuItems.forEach((item, index) => {
      if (item.value == this.props.value){
        menuItemIndex = index;
      }
    });
    return menuItemIndex
  }

  render() {
    let error =

;
    let labelStyle = {
      ...this.styles.label,
      ...this.props.labelStyle
    }
    if (this.props.errorText) {
      error =
{this.props.errorText}

      labelStyle = {
        ...labelStyle,
        color: BernieColors.red
      }
    }

    const menuItems = this.createMenuItems();
    let selectProps = {}
    Object.keys(this.props).forEach((key) => {
      if (key !== 'style' && key !== 'onChange' && key !== 'value' && key !== 'label')
        selectProps[key] = this.props[key]
    })
    return (

{this.props.label}

        <Select
          {...selectProps}
          labelKey="label"
          value={this.props.value}
          options={menuItems}
          onChange={(element) => {
            if (element)
              this.props.onChange(element.value)
            else
              this.props.onChange(null)
          }}
        />
        {error}

    )
  }
}<commit_msg>re-enable material-ui dropdown menu in GCSelectField<commit_after>import React from 'react';
import {FlatButton, SelectField} from 'material-ui';
import {BernieText, BernieColors} from '../styles/bernie-css'
import GCFormField from './GCFormField';

export default class GCSelectField extends GCFormField {
  styles = {
    label: {
      paddingBottom: 10,
      ...BernieText.inputLabel,
    },
  }

  createMenuItems() {
    return Object.keys(this.props.choices).map((choice) => {
      return {
        name: this.props.choices[choice],
        value: choice
      }
    })
  }

  getMenuItemIndex(menuItems) {
    let menuItemIndex = 0;
    menuItems.forEach((item, index) => {
      if (item.value == this.props.value){
        menuItemIndex = index;
      }
    });
    return menuItemIndex
  }

  render() {
    let error =

;
    let labelStyle = {
      ...this.styles.label,
      ...this.props.labelStyle
    }
    if (this.props.errorText) {
      error =
{this.props.errorText}

      labelStyle = {
        ...labelStyle,
        color: BernieColors.red
      }
    }

    const menuItems = this.createMenuItems();

    return (
    <SelectField
      // {...this.props}
      value={this.props.value}
      displayMember='name'
      valueMember='value'
      floatingLabelText={this.floatingLabelText()}
      errorStyle={BernieText.inputError}
      menuItems={menuItems}
      selectedIndex={this.getMenuItemIndex(menuItems)}
      onChange={(event) => {this.props.onChange(event.target.value)}}
    />
    )
  }
}<|endoftext|>


<commit_before>function createRetort(request, response, fn){
    return function(){
        var args = [].slice.call(arguments);

        args = [request, response].concat(args);

        fn.apply(null, args);
    };
};

function Retorter(retorts){
    this.retorts = {
        ok: this.ok,
        forbidden: this.forbidden,
        unauthorised: this.unauthorised,
        error: this.error,
        redirect: this.redirect
    };

    if(retorts){
        for(var key in retorts){
            this.retorts[key] = retorts[key];
        }
    }
}
Retorter.prototype.ok = function(request, response, data){
    response.writeHead(200);
    response.end(data);
};
Retorter.prototype.forbidden = function(request, response, message){
    response.writeHead(403, message);
    response.end();
};
Retorter.prototype.unauthorised = function(request, response, message){
    response.writeHead(301, message);
    response.end();
};
Retorter.prototype.error = function(request, response, error){
    response.writeHead(500);
    response.end(data);
};
Retorter.prototype.redirect = function(request, response, location){
    response.writeHead(301, {Location: location});
    response.end();
};
Retorter.prototype.retort = function(request, response){
    var retorts = {};

    for(var key in this.retorts){
        retorts[key] = createRetort(request, response, this.retorts[key]);
    }

    response.retort = retorts;
};

module.exports = Retorter;<commit_msg>fixed error bugh<commit_after>function createRetort(request, response, fn){
    return function(){
        var args = [].slice.call(arguments);

        args = [request, response].concat(args);

        fn.apply(null, args);
    };
};

function Retorter(retorts){
    this.retorts = {
        ok: this.ok,
        forbidden: this.forbidden,
        unauthorised: this.unauthorised,
        error: this.error,
        redirect: this.redirect
    };

    if(retorts){
        for(var key in retorts){
            this.retorts[key] = retorts[key];
        }
    }
}
Retorter.prototype.ok = function(request, response, data){
    response.writeHead(200);
    response.end(data);
};
Retorter.prototype.forbidden = function(request, response, message){
    response.writeHead(403, message);
    response.end();
};
Retorter.prototype.unauthorised = function(request, response, message){
    response.writeHead(301, message);
    response.end();
};
Retorter.prototype.error = function(request, response, error){
    response.writeHead(500);
    response.end(error);
};
Retorter.prototype.redirect = function(request, response, location){
    response.writeHead(301, {Location: location});
    response.end();
};
Retorter.prototype.retort = function(request, response){
    var retorts = {};

    for(var key in this.retorts){
        retorts[key] = createRetort(request, response, this.retorts[key]);
    }

    response.retort = retorts;
};

module.exports = Retorter;<|endoftext|>


<commit_before>module.exports = rimraf
rimraf.sync = rimrafSync

var assert = require("assert")
var path = require("path")
var fs = require("fs")
var glob = undefined
try {
  glob = require("glob")
} catch (_err) {
  // treat glob as optional.
}
var _0666 = parseInt('666', 8)

var defaultGlobOpts = {
  nosort: true,
  silent: true
}

// for EMFILE handling
var timeout = 0

var isWindows = (process.platform === "win32")

function defaults (options) {
  var methods = [
    'unlink',
    'chmod',
    'stat',
    'lstat',
    'rmdir',
    'readdir'
  ]
  methods.forEach(function(m) {
    options[m] = options[m] || fs[m]
    m = m + 'Sync'
    options[m] = options[m] || fs[m]
  })

  options.maxBusyTries = options.maxBusyTries || 3
  options.emfileWait = options.emfileWait || 1000
  if (options.glob === false) {
    options.disableGlob = true
  }
  if (options.disableGlob !== true && glob === undefined) {
    throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')
  }
  options.disableGlob = options.disableGlob || false
  options.glob = options.glob || defaultGlobOpts
}

function rimraf (p, options, cb) {
  if (typeof options === 'function') {
    cb = options
    options = {}
  }

  assert(p, 'rimraf: missing path')
  assert.equal(typeof p, 'string', 'rimraf: path should be a string')
  assert.equal(typeof cb, 'function', 'rimraf: callback function required')
  assert(options, 'rimraf: invalid options argument provided')
  assert.equal(typeof options, 'object', 'rimraf: options should be object')

  defaults(options)

  var busyTries = 0
  var errState = null
  var n = 0

  if (options.disableGlob || !glob.hasMagic(p))
    return afterGlob(null, [p])

  options.lstat(p, function (er, stat) {
    if (!er)
      return afterGlob(null, [p])

    glob(p, options.glob, afterGlob)
  })

  function next (er) {
    errState = errState || er
    if (--n === 0)
      cb(errState)
  }

  function afterGlob (er, results) {
    if (er)
      return cb(er)

    n = results.length
    if (n === 0)
      return cb()

    results.forEach(function (p) {
      rimraf_(p, options, function CB (er) {
        if (er) {
          if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&
              busyTries < options.maxBusyTries) {
            busyTries ++
            var time = busyTries * 100
            // try again, with the same exact callback as this one.
            return setTimeout(function () {
              rimraf_(p, options, CB)
            }, time)
          }

          // this one won't happen if graceful-fs is used.
          if (er.code === "EMFILE" && timeout < options.emfileWait) {
            return setTimeout(function () {
              rimraf_(p, options, CB)
            }, timeout ++)
          }

          // already gone
          if (er.code === "ENOENT") er = null
        }

        timeout = 0
        next(er)
      })
    })
  }
}

// Two possible strategies.
// 1. Assume it's a file.  unlink it, then do the dir stuff on EPERM or EISDIR
// 2. Assume it's a directory.  readdir, then do the file stuff on ENOTDIR
//
// Both result in an extra syscall when you guess wrong.  However, there
// are likely far more normal files in the world than directories.  This
// is based on the assumption that a the average number of files per
// directory is >= 1.
//
// If anyone ever complains about this, then I guess the strategy could
// be made configurable somehow.  But until then, YAGNI.
function rimraf_ (p, options, cb) {
  assert(p)
  assert(options)
  assert(typeof cb === 'function')

  // sunos lets the root user unlink directories, which is... weird.
  // so we have to lstat here and make sure it's not a dir.
  options.lstat(p, function (er, st) {
    if (er && er.code === "ENOENT")
      return cb(null)

    // Windows can EPERM on stat.  Life is suffering.
    if (er && er.code === "EPERM" && isWindows)
      fixWinEPERM(p, options, er, cb)

    if (st && st.isDirectory())
      return rmdir(p, options, er, cb)

    options.unlink(p, function (er) {
      if (er) {
        if (er.code === "ENOENT")
          return cb(null)
        if (er.code === "EPERM")
          return (isWindows)
            ? fixWinEPERM(p, options, er, cb)
            : rmdir(p, options, er, cb)
        if (er.code === "EISDIR")
          return rmdir(p, options, er, cb)
      }
      return cb(er)
    })
  })
}

function fixWinEPERM (p, options, er, cb) {
  assert(p)
  assert(options)
  assert(typeof cb === 'function')
  if (er)
    assert(er instanceof Error)

  options.chmod(p, _0666, function (er2) {
    if (er2)
      cb(er2.code === "ENOENT" ? null : er)
    else
      options.stat(p, function(er3, stats) {
        if (er3)
          cb(er3.code === "ENOENT" ? null : er)
        else if (stats.isDirectory())
          rmdir(p, options, er, cb)
        else
          options.unlink(p, cb)
      })
  })
}

function fixWinEPERMSync (p, options, er) {
  assert(p)
  assert(options)
  if (er)
    assert(er instanceof Error)

  try {
    options.chmodSync(p, _0666)
  } catch (er2) {
    if (er2.code === "ENOENT")
      return
    else
      throw er
  }

  try {
    var stats = options.statSync(p)
  } catch (er3) {
    if (er3.code === "ENOENT")
      return
    else
      throw er
  }

  if (stats.isDirectory())
    rmdirSync(p, options, er)
  else
    options.unlinkSync(p)
}

function rmdir (p, options, originalEr, cb) {
  assert(p)
  assert(options)
  if (originalEr)
    assert(originalEr instanceof Error)
  assert(typeof cb === 'function')

  // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
  // if we guessed wrong, and it's not a directory, then
  // raise the original error.
  options.rmdir(p, function (er) {
    if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
      rmkids(p, options, cb)
    else if (er && er.code === "ENOTDIR")
      cb(originalEr)
    else
      cb(er)
  })
}

function rmkids(p, options, cb) {
  assert(p)
  assert(options)
  assert(typeof cb === 'function')

  options.readdir(p, function (er, files) {
    if (er)
      return cb(er)
    var n = files.length
    if (n === 0)
      return options.rmdir(p, cb)
    var errState
    files.forEach(function (f) {
      rimraf(path.join(p, f), options, function (er) {
        if (errState)
          return
        if (er)
          return cb(errState = er)
        if (--n === 0)
          options.rmdir(p, cb)
      })
    })
  })
}

// this looks simpler, and is strictly *faster*, but will
// tie up the JavaScript thread and fail on excessively
// deep directory trees.
function rimrafSync (p, options) {
  options = options || {}
  defaults(options)

  assert(p, 'rimraf: missing path')
  assert.equal(typeof p, 'string', 'rimraf: path should be a string')
  assert(options, 'rimraf: missing options')
  assert.equal(typeof options, 'object', 'rimraf: options should be object')

  var results

  if (options.disableGlob || !glob.hasMagic(p)) {
    results = [p]
  } else {
    try {
      options.lstatSync(p)
      results = [p]
    } catch (er) {
      results = glob.sync(p, options.glob)
    }
  }

  if (!results.length)
    return

  for (var i = 0; i < results.length; i++) {
    var p = results[i]

    try {
      var st = options.lstatSync(p)
    } catch (er) {
      if (er.code === "ENOENT")
        return

      // Windows can EPERM on stat.  Life is suffering.
      if (er.code === "EPERM" && isWindows)
        fixWinEPERMSync(p, options, er)
    }

    try {
      // sunos lets the root user unlink directories, which is... weird.
      if (st && st.isDirectory())
        rmdirSync(p, options, null)
      else
        options.unlinkSync(p)
    } catch (er) {
      if (er.code === "ENOENT")
        return
      if (er.code === "EPERM")
        return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
      if (er.code !== "EISDIR")
        throw er

      rmdirSync(p, options, er)
    }
  }
}

function rmdirSync (p, options, originalEr) {
  assert(p)
  assert(options)
  if (originalEr)
    assert(originalEr instanceof Error)

  try {
    options.rmdirSync(p)
  } catch (er) {
    if (er.code === "ENOENT")
      return
    if (er.code === "ENOTDIR")
      throw originalEr
    if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
      rmkidsSync(p, options)
  }
}

function rmkidsSync (p, options) {
  assert(p)
  assert(options)
  options.readdirSync(p).forEach(function (f) {
    rimrafSync(path.join(p, f), options)
  })

  // We only end up here once we got ENOTEMPTY at least once, and
  // at this point, we are guaranteed to have removed all the kids.
  // So, we know that it won't be ENOENT or ENOTDIR or anything else.
  // try really hard to delete stuff on windows, because it has a
  // PROFOUNDLY annoying habit of not closing handles promptly when
  // files are deleted, resulting in spurious ENOTEMPTY errors.
  var retries = isWindows ? 100 : 1
  var i = 0
  do {
    var threw = true
    try {
      var ret = options.rmdirSync(p, options)
      threw = false
      return ret
    } finally {
      if (++i < retries && threw)
        continue
    }
  } while (true)
}
<commit_msg>Use const/let<commit_after>module.exports = rimraf
rimraf.sync = rimrafSync

const assert = require("assert")
const path = require("path")
const fs = require("fs")
let glob = undefined
try {
  glob = require("glob")
} catch (_err) {
  // treat glob as optional.
}

const defaultGlobOpts = {
  nosort: true,
  silent: true
}

// for EMFILE handling
let timeout = 0

const isWindows = (process.platform === "win32")

function defaults (options) {
  const methods = [
    'unlink',
    'chmod',
    'stat',
    'lstat',
    'rmdir',
    'readdir'
  ]
  methods.forEach(function(m) {
    options[m] = options[m] || fs[m]
    m = m + 'Sync'
    options[m] = options[m] || fs[m]
  })

  options.maxBusyTries = options.maxBusyTries || 3
  options.emfileWait = options.emfileWait || 1000
  if (options.glob === false) {
    options.disableGlob = true
  }
  if (options.disableGlob !== true && glob === undefined) {
    throw Error('glob dependency not found, set `options.disableGlob = true` if intentional')
  }
  options.disableGlob = options.disableGlob || false
  options.glob = options.glob || defaultGlobOpts
}

function rimraf (p, options, cb) {
  if (typeof options === 'function') {
    cb = options
    options = {}
  }

  assert(p, 'rimraf: missing path')
  assert.equal(typeof p, 'string', 'rimraf: path should be a string')
  assert.equal(typeof cb, 'function', 'rimraf: callback function required')
  assert(options, 'rimraf: invalid options argument provided')
  assert.equal(typeof options, 'object', 'rimraf: options should be object')

  defaults(options)

  let busyTries = 0
  let errState = null
  let n = 0

  if (options.disableGlob || !glob.hasMagic(p))
    return afterGlob(null, [p])

  options.lstat(p, function (er, stat) {
    if (!er)
      return afterGlob(null, [p])

    glob(p, options.glob, afterGlob)
  })

  function next (er) {
    errState = errState || er
    if (--n === 0)
      cb(errState)
  }

  function afterGlob (er, results) {
    if (er)
      return cb(er)

    n = results.length
    if (n === 0)
      return cb()

    results.forEach(function (p) {
      rimraf_(p, options, function CB (er) {
        if (er) {
          if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") &&
              busyTries < options.maxBusyTries) {
            busyTries ++
            // try again, with the same exact callback as this one.
            return setTimeout(function () {
              rimraf_(p, options, CB)
            }, busyTries * 100)
          }

          // this one won't happen if graceful-fs is used.
          if (er.code === "EMFILE" && timeout < options.emfileWait) {
            return setTimeout(function () {
              rimraf_(p, options, CB)
            }, timeout ++)
          }

          // already gone
          if (er.code === "ENOENT") er = null
        }

        timeout = 0
        next(er)
      })
    })
  }
}

// Two possible strategies.
// 1. Assume it's a file.  unlink it, then do the dir stuff on EPERM or EISDIR
// 2. Assume it's a directory.  readdir, then do the file stuff on ENOTDIR
//
// Both result in an extra syscall when you guess wrong.  However, there
// are likely far more normal files in the world than directories.  This
// is based on the assumption that a the average number of files per
// directory is >= 1.
//
// If anyone ever complains about this, then I guess the strategy could
// be made configurable somehow.  But until then, YAGNI.
function rimraf_ (p, options, cb) {
  assert(p)
  assert(options)
  assert(typeof cb === 'function')

  // sunos lets the root user unlink directories, which is... weird.
  // so we have to lstat here and make sure it's not a dir.
  options.lstat(p, function (er, st) {
    if (er && er.code === "ENOENT")
      return cb(null)

    // Windows can EPERM on stat.  Life is suffering.
    if (er && er.code === "EPERM" && isWindows)
      fixWinEPERM(p, options, er, cb)

    if (st && st.isDirectory())
      return rmdir(p, options, er, cb)

    options.unlink(p, function (er) {
      if (er) {
        if (er.code === "ENOENT")
          return cb(null)
        if (er.code === "EPERM")
          return (isWindows)
            ? fixWinEPERM(p, options, er, cb)
            : rmdir(p, options, er, cb)
        if (er.code === "EISDIR")
          return rmdir(p, options, er, cb)
      }
      return cb(er)
    })
  })
}

function fixWinEPERM (p, options, er, cb) {
  assert(p)
  assert(options)
  assert(typeof cb === 'function')
  if (er)
    assert(er instanceof Error)

  options.chmod(p, 0o666, function (er2) {
    if (er2)
      cb(er2.code === "ENOENT" ? null : er)
    else
      options.stat(p, function(er3, stats) {
        if (er3)
          cb(er3.code === "ENOENT" ? null : er)
        else if (stats.isDirectory())
          rmdir(p, options, er, cb)
        else
          options.unlink(p, cb)
      })
  })
}

function fixWinEPERMSync (p, options, er) {
  assert(p)
  assert(options)
  if (er)
    assert(er instanceof Error)

  try {
    options.chmodSync(p, 0o666)
  } catch (er2) {
    if (er2.code === "ENOENT")
      return
    else
      throw er
  }

  let stats
  try {
    stats = options.statSync(p)
  } catch (er3) {
    if (er3.code === "ENOENT")
      return
    else
      throw er
  }

  if (stats.isDirectory())
    rmdirSync(p, options, er)
  else
    options.unlinkSync(p)
}

function rmdir (p, options, originalEr, cb) {
  assert(p)
  assert(options)
  if (originalEr)
    assert(originalEr instanceof Error)
  assert(typeof cb === 'function')

  // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
  // if we guessed wrong, and it's not a directory, then
  // raise the original error.
  options.rmdir(p, function (er) {
    if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM"))
      rmkids(p, options, cb)
    else if (er && er.code === "ENOTDIR")
      cb(originalEr)
    else
      cb(er)
  })
}

function rmkids(p, options, cb) {
  assert(p)
  assert(options)
  assert(typeof cb === 'function')

  options.readdir(p, function (er, files) {
    if (er)
      return cb(er)
    let n = files.length
    if (n === 0)
      return options.rmdir(p, cb)
    let errState
    files.forEach(function (f) {
      rimraf(path.join(p, f), options, function (er) {
        if (errState)
          return
        if (er)
          return cb(errState = er)
        if (--n === 0)
          options.rmdir(p, cb)
      })
    })
  })
}

// this looks simpler, and is strictly *faster*, but will
// tie up the JavaScript thread and fail on excessively
// deep directory trees.
function rimrafSync (p, options) {
  options = options || {}
  defaults(options)

  assert(p, 'rimraf: missing path')
  assert.equal(typeof p, 'string', 'rimraf: path should be a string')
  assert(options, 'rimraf: missing options')
  assert.equal(typeof options, 'object', 'rimraf: options should be object')

  let results

  if (options.disableGlob || !glob.hasMagic(p)) {
    results = [p]
  } else {
    try {
      options.lstatSync(p)
      results = [p]
    } catch (er) {
      results = glob.sync(p, options.glob)
    }
  }

  if (!results.length)
    return

  for (let i = 0; i < results.length; i++) {
    const p = results[i]

    let st
    try {
      st = options.lstatSync(p)
    } catch (er) {
      if (er.code === "ENOENT")
        return

      // Windows can EPERM on stat.  Life is suffering.
      if (er.code === "EPERM" && isWindows)
        fixWinEPERMSync(p, options, er)
    }

    try {
      // sunos lets the root user unlink directories, which is... weird.
      if (st && st.isDirectory())
        rmdirSync(p, options, null)
      else
        options.unlinkSync(p)
    } catch (er) {
      if (er.code === "ENOENT")
        return
      if (er.code === "EPERM")
        return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
      if (er.code !== "EISDIR")
        throw er

      rmdirSync(p, options, er)
    }
  }
}

function rmdirSync (p, options, originalEr) {
  assert(p)
  assert(options)
  if (originalEr)
    assert(originalEr instanceof Error)

  try {
    options.rmdirSync(p)
  } catch (er) {
    if (er.code === "ENOENT")
      return
    if (er.code === "ENOTDIR")
      throw originalEr
    if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")
      rmkidsSync(p, options)
  }
}

function rmkidsSync (p, options) {
  assert(p)
  assert(options)
  options.readdirSync(p).forEach(function (f) {
    rimrafSync(path.join(p, f), options)
  })

  // We only end up here once we got ENOTEMPTY at least once, and
  // at this point, we are guaranteed to have removed all the kids.
  // So, we know that it won't be ENOENT or ENOTDIR or anything else.
  // try really hard to delete stuff on windows, because it has a
  // PROFOUNDLY annoying habit of not closing handles promptly when
  // files are deleted, resulting in spurious ENOTEMPTY errors.
  const retries = isWindows ? 100 : 1
  let i = 0
  do {
    let threw = true
    try {
      const ret = options.rmdirSync(p, options)
      threw = false
      return ret
    } finally {
      if (++i < retries && threw)
        continue
    }
  } while (true)
}
<|endoftext|>


<commit_before>/*!
 * Ripper.js v0.1
 * Copyright 2012, Zbigniew Tenerowicz, naugtur.pl
 * MIT License, see license.txt
 */

var Ripper = function(S) {
  'use strict';

  S || (S = {});
  S.dictionary || (S.dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!~*;:/$,-_");
  S.numberLength || (S.numberLength = 2);
  S.heuristic || (S.heuristic = false);
  S.keepJS || (S.keepJS = false);
  //------------------------------------------------------------------- /Math
  var M = (function() {
    var c = (S.dictionary).split(""),
    b = S.dictionary.length;

    //creates a converter function that translates numbers to base(dictionary.length) which can be more than base64

    function makeConverter(l) { //factory for a l-character converter
      var max;

      if (l === 2) { //case of 2 chars - unrolled for performance
        max = b * b;
        return function(n) {
          if (typeof(n) !== 'number') {
            return ''
          }
          var s = "";
          if (n >= max) {
            throw 'Number too big to convert. Use bigger dictionary or numberLength.';
          }
          s = c[n % b];
          n = Math.floor(n / b);
          s = c[n % b] + s;
          return s;
        };
      } else {
        max = Math.pow(b, l); //init once
        return function(n) {
          if (typeof(n) !== 'number') {
            return ''
          }
          var s = "";
          if (n >= max) {
            throw 'Number too big to convert. Use bigger dictionary or numberLength.';
          }
          for (var i = 0; i < l; i += 1) {
            s = c[n % b] + s;
            n = Math.floor(n / b);
          }
          return s;
        };
      }
    }

    //for any length of input converts it to proper integer based on dictionary

    function reverseConverter(x) {
      var ret = 0;
      for (var i = 1; x.length > 0; i *= b) {
        ret += S.dictionary.indexOf(x.charAt(x.length - 1)) * i;
        x = x.substr(0, x.length - 1);
      }
      return ret;
    }

    return {
      makeConverter: makeConverter,
      reverseConverter: reverseConverter
    }
  })();

  //------------------------------------------------------------------- /CSS
  var CSS = (function() {

    var defaultsStore = {};

    //change aaa-bbb into aaaBbb
    function camelize(prop) {
      var rep = function(a, b) {
        return b.toUpperCase();
      };
      return prop.replace(/\-([a-z])/g, rep);
    };

    function getStyleObject(dom) {
      var style, returns = {};
      if (window.getComputedStyle) {
        style = window.getComputedStyle(dom, null);
        for (var i = 0, l = style.length; i < l; i += 1) {
          var prop = style[i],
          val = style.getPropertyValue(prop);
          returns[prop] = val;
        }
        return returns;
      }
      if (style = dom.currentStyle) {
        for (var prop in style) {
          returns[prop] = style[prop];
        }
        return returns;
      }
      if (style = dom.style) {
        for (var prop in style) {
          if (typeof style[prop] != 'function') {
            returns[prop] = style[prop];
          };
        };
        return returns;
      };
      return returns;

    }

    //Single instance sandbox iframe
    var getSandbox = (function() {
      var ifr, iframeSandbox = false;
      ifr = document.createElement('iframe');
      ifr.style.display = 'none';
      document.documentElement.appendChild(ifr);
      return function() {
        if (iframeSandbox) {
          return iframeSandbox;
        } else {

          iframeSandbox = ifr.contentDocument.body;
          return iframeSandbox;
        }
      };
    })();

    //memoized function to get the default style of an element
    //TODO [not sure anymore] prefetch styles for some form elements eg. putting OPTION in SELECT

    function memDefaults(tag, type) {
      var key = tag + type;
      if (defaultsStore[key]) {
        return defaultsStore[key];
      } else {
        //get styles for defaults
        var sandBox = getSandbox(),
        e = document.createElement(tag);
        type && (e.type = type);
        e.style.visibility = 'hidden';
        sandBox.appendChild(e);
        defaultsStore[key] = getStyleObject(e);
        defaultsStore[key].visibility = 'visible';
        sandBox.removeChild(e);
        return defaultsStore[key];
      }
    }

    //returns non-default styles for element
    // e is the element

    function getDiff(e) {
      var styles = getStyleObject(e),
      nn = e.nodeName,
      defaults, r = false; //result not empty
      if (nn === 'INPUT') {
        defaults = memDefaults(nn, e.getAttribute('type'));
      } else {
        defaults = memDefaults(nn, '');
      }

      for (var i in defaults) {
        if (defaults.hasOwnProperty(i)) {
          if (styles[i] === defaults[i]) {
            delete styles[i];
          } else {
            if(i.substr(0,1)==='-'){
              styles[i.replace(/^-[a-z]*-/,'')]=styles[i];
              delete(styles[i]);
            }
            //something remains
            r = true;

          }
        }
      }

      if(styles['cssFloat']){
          styles['float']=styles['cssFloat'];
          delete styles['cssFloat'];
      }

      return (r) ? styles : {};
    }

    return {
      get: getDiff,
      camelize:camelize
    }

  })();

  //------------------------------------------------------------------- /Heuristic Compress
  var Heuristic = (function() {

    var z = {},
    dictionary = ['color', 'div', 'border', 'font', 'text', 'origin', 'left', 'width', 'right', 'bottom', 'size', 'height', 'family', 'padding', 'transform', 'perspective', 'align', 'none', 'type', 'option', 'background', 'value', 'collapse', 'margin', 'outline', 'display', 'serif', 'sans', 'solid', 'spacing', 'cursor', 'href', 'Arial', 'auto', 'position', 'block', 'vertical', 'Tahoma', 'span', 'name', 'input', 'line', 'default', 'float', 'label', 'Helvetica', 'hidden', 'horizontal', 'repeat', 'center', 'absolute', 'Verdana', 'recaptcha', 'overflow', 'image', 'relative'],
    //some popular longer words in html and css
    keys = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); //71
    //naive compression for html and CSS

    function compress(text) {
      var result;
      result = text.replace(/~/g, '~~'); //Not too often I guess
      for (var i = 0, l = dictionary.length; i < l; i += 1) {
        result.replace(RegExp(dictionary[i], 'g'), '~' + keys[i]);
      }
      return result
    }

    //decompression

    function decompress(text) {
      var result = text;
      for (var i = 0, l = dictionary.length; i < l; i += 1) {
        result.replace(RegExp('~' + keys[i], 'g'), dictionary[i]);
      }
      result.replace(/~~/g, '~');
      return result
    }

    return {
      compress: compress,
      decompress: decompress
    }
  })()

  //------------------------------------------------------------------- /LZW
  var LZW = (function() {
    //setup for basic dictionary
    var rootDict = 382;
    //good characters: ((n>31 && n<127)||n>=160) //TODO later?

    function compress(uncompressed) {
      // Build the dictionary.
      var i, dictionary = {},
      c, wc, w = "",
      result = [],
      dictSize = rootDict;
      for (i = 0; i < rootDict; i += 1) {
        dictionary[String.fromCharCode(i)] = i;
      }

      for (i = 0; i < uncompressed.length; i += 1) {
        c = uncompressed.charAt(i);
        if (c.charCodeAt(0) > rootDict) {
          continue;
        }
        wc = w + c;
        if (dictionary[wc]) {
          w = wc;
        } else {
          result[result.length] = dictionary[w];
          // Add wc to the dictionary.
          dictionary[wc] = dictSize++;
          w = String(c);
        }
      }

      // Output the code for w.
      if (w !== "") {
        result[result.length] = dictionary[w];
      }
      return result;
    }

    function decompress(compressed) {
      // Build the dictionary.
      var i, dictionary = [],
      w, result, k, entry = "",
      dictSize = rootDict;
      for (i = 0; i < rootDict; i += 1) {
        dictionary[i] = String.fromCharCode(i);
      }

      w = String.fromCharCode(compressed[0]);
      result = w;
      for (i = 1; i < compressed.length; i += 1) {
        k = compressed[i];
        if (dictionary[k]) {
          entry = dictionary[k];
        } else {
          if (k === dictSize) {
            entry = w + w.charAt(0);
          } else {
            throw "Unexpected character in decompression";
            return;
          }
        }

        result += entry;

        // Add w+entry[0] to the dictionary.
        dictionary[dictSize++] = w + entry.charAt(0);

        w = entry;
      }
      return result;
    }

    return {
      compress: function(txt, cV) {
        return compress(txt).map(function(a) {
          return cV(a)
        }).join('');
      },
      decompress: function(data, dV, chunkSize) {
        //split into chunks of characters
        var tmp = [];
        data = data.split('');
        while (data.length) {
          tmp[tmp.length] = dV(data.splice(0, chunkSize).join(''));
        }

        return decompress(tmp);
      }
    };

  })();

  //------------------------------------------------------------------- /Main
  //creates a function that runs a callback for every node, recursively, and provides identification

  function makeRecursiveTraverser(callback) {
    var action = callback,
    cV = M.makeConverter(2);

    function recur(e, n, k, u) {
      var x, id = n + '' + cV(k);
      //doing stuff
      action(e, id);

      //go deeper
      k = 0;
      x = e.firstChild;
      for (; x; x = x.nextSibling) {
        if (x !== u && x.nodeType === 1) {
          recur(x, id, ++k);
        }
      }
    }
    return recur;
  }

  function rip(node,preprocess) {
    var htmlContent, rippedData, tmpdom, scripts;

    tmpdom = document.createElement(node.nodeName);
    tmpdom.innerHTML = node.innerHTML;
     if( typeof(preprocess) == 'function' ){
            preprocess(tmpdom);
        }

   if(S.keepJS){
        //no need for style and class
        htmlContent = tmpdom.innerHTML.replace(/(style)=("[^"<]*")|('[^'<]*')/gi, '')
    }else{
        scripts = tmpdom.getElementsByTagName('script');
        var i = scripts.length;
        while (i--) {
          scripts[i].parentNode.removeChild(scripts[i]);
        }
        //no need for style and class, drop events
        htmlContent = tmpdom.innerHTML.replace(/(style|on[^ =]*)=("[^"<]*")|('[^'<]*')/gi, '');
    }

    //; ignore whitespace
    htmlContent = htmlContent.replace(/\s+/g, ' ');

    rippedData = {
      html: htmlContent,
      css: {}
    };
    //store CSS recursively
    makeRecursiveTraverser(function(e, id) {
      rippedData.css[id] = CSS.get(e);
    })(node, '', 1);

    var fragment = JSON.stringify(rippedData);
    //console.log(fragment);
    if (S.heuristic) {
      fragment = Heuristic.compress(fragment);
    }

    var compressed = LZW.compress(fragment, M.makeConverter(S.numberLength));

    return compressed;

  }

  function put(data,target) {
    var node,findTR,nodeName = 'div',
        obj = LZW.decompress(data, M.reverseConverter, S.numberLength);
    if (S.heuristic) {
      obj = Heuristic.decompress(obj);
    }
    obj = JSON.parse(obj);
    //if code starts from tr, it needs a table tag to work
    findTR = obj.html.substring(0,5);
    if(findTR.indexOf('<tr')>-1 || findTR.indexOf('<th')>-1 || findTR.indexOf('<td')>-1 ){
      nodeName = 'table';
    }

    node = document.createElement(nodeName);
    node.innerHTML = obj.html;
    //set css back, recursively
    makeRecursiveTraverser(function(e, id) {
      var i,css = obj.css[id];
      for (var p in css) {
        i=CSS.camelize(p);
        e.style[i] = css[p];
        if(i==='float'){
            e.style['cssFloat'] = css[p];
        }
      }
    })(node, '', 1);

    if(target){
      target.appendChild(node);
    }
    return node;

  }

  return {
    copy: rip,
    paste: put,
    test: function(fragment) {
      var test = LZW.compress(fragment, M.makeConverter(S.numberLength));
      return [test, LZW.decompress(test, M.reverseConverter, S.numberLength)];
    }
  };

}

/*
init example:
var ripper=Ripper({
  dictionary: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!~*;:/$,"
});

*/<commit_msg>Try to support CSS vendor prefixes between different browsers<commit_after>/*!
 * Ripper.js v0.1
 * Copyright 2012, Zbigniew Tenerowicz, naugtur.pl
 * MIT License, see license.txt
 */

var Ripper = function(S) {
  'use strict';

  S || (S = {});
  S.dictionary || (S.dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!~*;:/$,-_");
  S.numberLength || (S.numberLength = 2);
  S.heuristic || (S.heuristic = false);
  S.keepJS || (S.keepJS = false);
  //------------------------------------------------------------------- /Math
  var M = (function() {
    var c = (S.dictionary).split(""),
    b = S.dictionary.length;

    //creates a converter function that translates numbers to base(dictionary.length) which can be more than base64

    function makeConverter(l) { //factory for a l-character converter
      var max;

      if (l === 2) { //case of 2 chars - unrolled for performance
        max = b * b;
        return function(n) {
          if (typeof(n) !== 'number') {
            return ''
          }
          var s = "";
          if (n >= max) {
            throw 'Number too big to convert. Use bigger dictionary or numberLength.';
          }
          s = c[n % b];
          n = Math.floor(n / b);
          s = c[n % b] + s;
          return s;
        };
      } else {
        max = Math.pow(b, l); //init once
        return function(n) {
          if (typeof(n) !== 'number') {
            return ''
          }
          var s = "";
          if (n >= max) {
            throw 'Number too big to convert. Use bigger dictionary or numberLength.';
          }
          for (var i = 0; i < l; i += 1) {
            s = c[n % b] + s;
            n = Math.floor(n / b);
          }
          return s;
        };
      }
    }

    //for any length of input converts it to proper integer based on dictionary

    function reverseConverter(x) {
      var ret = 0;
      for (var i = 1; x.length > 0; i *= b) {
        ret += S.dictionary.indexOf(x.charAt(x.length - 1)) * i;
        x = x.substr(0, x.length - 1);
      }
      return ret;
    }

    return {
      makeConverter: makeConverter,
      reverseConverter: reverseConverter
    }
  })();

  //------------------------------------------------------------------- /CSS
  var CSS = (function() {

    var defaultsStore = {}, prefix;

    function getVendorPrefix() {
      if (prefix) return prefix;

      var regex = /^(Moz|Webkit|Khtml|O|ms)(?=[A-Z])/;

      var someScript = document.getElementsByTagName('script')[0];

      for(var prop in someScript.style) {
        if(regex.test(prop)) {
          // test is faster than match, so it's better to perform
          // that on the lot and match only when necessary
          return prefix = prop.match(regex)[0];
        }
      }

      // Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
      // However (prop in style) returns the correct value, so we'll have to test for
      // the precence of a specific property
      if('WebkitOpacity' in someScript.style) return prefix = 'Webkit';
      if('KhtmlOpacity' in someScript.style) return prefix = 'Khtml';

      return prefix = '';
    }

    //replace -rr- prefix to the one used by current browser, or to optionally specified one
    function prefixify(value, prefix) {
      if (typeof prefix === "undefined") {
        prefix = '-' + getVendorPrefix().toLowerCase() + '-';
      }
      return value.replace(/(^|[\s,])-rr-/, prefix);
    }

    //change aaa-bbb into aaaBbb
    function camelize(prop) {
      prop = prefixify(prop, getVendorPrefix()+'-'); // we can't use -prefix- notation here, because in JS side one of the prefixes starts with lowercase. It's "ms" - what a surprise!
      var rep = function(a, b) {
        return b.toUpperCase();
      };
      return prop.replace(/\-([a-z])/g, rep);
    };

    function getStyleObject(dom) {
      var style, returns = {};
      if (window.getComputedStyle) {
        style = window.getComputedStyle(dom, null);
        for (var i = 0, l = style.length; i < l; i += 1) {
          var prop = style[i],
          val = style.getPropertyValue(prop);
          returns[prop] = val;
        }
        return returns;
      }
      if (style = dom.currentStyle) {
        for (var prop in style) {
          returns[prop] = style[prop];
        }
        return returns;
      }
      if (style = dom.style) {
        for (var prop in style) {
          if (typeof style[prop] != 'function') {
            returns[prop] = style[prop];
          };
        };
        return returns;
      };
      return returns;

    }

    //Single instance sandbox iframe
    var getSandbox = (function() {
      var ifr, iframeSandbox = false;
      ifr = document.createElement('iframe');
      ifr.style.display = 'none';
      document.documentElement.appendChild(ifr);
      return function() {
        if (iframeSandbox) {
          return iframeSandbox;
        } else {

          iframeSandbox = ifr.contentDocument.body;
          return iframeSandbox;
        }
      };
    })();

    //memoized function to get the default style of an element
    //TODO [not sure anymore] prefetch styles for some form elements eg. putting OPTION in SELECT

    function memDefaults(tag, type) {
      var key = tag + type;
      if (defaultsStore[key]) {
        return defaultsStore[key];
      } else {
        //get styles for defaults
        var sandBox = getSandbox(),
        e = document.createElement(tag);
        type && (e.type = type);
        e.style.visibility = 'hidden';
        sandBox.appendChild(e);
        defaultsStore[key] = getStyleObject(e);
        defaultsStore[key].visibility = 'visible';
        sandBox.removeChild(e);
        return defaultsStore[key];
      }
    }

    //returns non-default styles for element
    // e is the element

    function getDiff(e) {
      var styles = getStyleObject(e),
      nn = e.nodeName,
      defaults, r = false; //result not empty
      if (nn === 'INPUT') {
        defaults = memDefaults(nn, e.getAttribute('type'));
      } else {
        defaults = memDefaults(nn, '');
      }

      for (var i in defaults) {
        if (defaults.hasOwnProperty(i)) {
          if (styles[i] === defaults[i]) {
            delete styles[i];
          } else {
            styles[i]=prefixify(styles[i], '-rr-');
            if(i.substr(0,1)==='-'){
              styles[i.replace(/^-[a-z]*-/,'-rr-')]=styles[i];
              delete(styles[i]);
            }
            //something remains
            r = true;

          }
        }
      }

      if(styles['cssFloat']){
          styles['float']=styles['cssFloat'];
          delete styles['cssFloat'];
      }

      return (r) ? styles : {};
    }

    return {
      get: getDiff,
      camelize:camelize,
      prefixify: prefixify
    }

  })();

  //------------------------------------------------------------------- /Heuristic Compress
  var Heuristic = (function() {

    var z = {},
    dictionary = ['color', 'div', 'border', 'font', 'text', 'origin', 'left', 'width', 'right', 'bottom', 'size', 'height', 'family', 'padding', 'transform', 'perspective', 'align', 'none', 'type', 'option', 'background', 'value', 'collapse', 'margin', 'outline', 'display', 'serif', 'sans', 'solid', 'spacing', 'cursor', 'href', 'Arial', 'auto', 'position', 'block', 'vertical', 'Tahoma', 'span', 'name', 'input', 'line', 'default', 'float', 'label', 'Helvetica', 'hidden', 'horizontal', 'repeat', 'center', 'absolute', 'Verdana', 'recaptcha', 'overflow', 'image', 'relative'],
    //some popular longer words in html and css
    keys = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); //71
    //naive compression for html and CSS

    function compress(text) {
      var result;
      result = text.replace(/~/g, '~~'); //Not too often I guess
      for (var i = 0, l = dictionary.length; i < l; i += 1) {
        result.replace(RegExp(dictionary[i], 'g'), '~' + keys[i]);
      }
      return result
    }

    //decompression

    function decompress(text) {
      var result = text;
      for (var i = 0, l = dictionary.length; i < l; i += 1) {
        result.replace(RegExp('~' + keys[i], 'g'), dictionary[i]);
      }
      result.replace(/~~/g, '~');
      return result
    }

    return {
      compress: compress,
      decompress: decompress
    }
  })()

  //------------------------------------------------------------------- /LZW
  var LZW = (function() {
    //setup for basic dictionary
    var rootDict = 382;
    //good characters: ((n>31 && n<127)||n>=160) //TODO later?

    function compress(uncompressed) {
      // Build the dictionary.
      var i, dictionary = {},
      c, wc, w = "",
      result = [],
      dictSize = rootDict;
      for (i = 0; i < rootDict; i += 1) {
        dictionary[String.fromCharCode(i)] = i;
      }

      for (i = 0; i < uncompressed.length; i += 1) {
        c = uncompressed.charAt(i);
        if (c.charCodeAt(0) > rootDict) {
          continue;
        }
        wc = w + c;
        if (dictionary[wc]) {
          w = wc;
        } else {
          result[result.length] = dictionary[w];
          // Add wc to the dictionary.
          dictionary[wc] = dictSize++;
          w = String(c);
        }
      }

      // Output the code for w.
      if (w !== "") {
        result[result.length] = dictionary[w];
      }
      return result;
    }

    function decompress(compressed) {
      // Build the dictionary.
      var i, dictionary = [],
      w, result, k, entry = "",
      dictSize = rootDict;
      for (i = 0; i < rootDict; i += 1) {
        dictionary[i] = String.fromCharCode(i);
      }

      w = String.fromCharCode(compressed[0]);
      result = w;
      for (i = 1; i < compressed.length; i += 1) {
        k = compressed[i];
        if (dictionary[k]) {
          entry = dictionary[k];
        } else {
          if (k === dictSize) {
            entry = w + w.charAt(0);
          } else {
            throw "Unexpected character in decompression";
            return;
          }
        }

        result += entry;

        // Add w+entry[0] to the dictionary.
        dictionary[dictSize++] = w + entry.charAt(0);

        w = entry;
      }
      return result;
    }

    return {
      compress: function(txt, cV) {
        return compress(txt).map(function(a) {
          return cV(a)
        }).join('');
      },
      decompress: function(data, dV, chunkSize) {
        //split into chunks of characters
        var tmp = [];
        data = data.split('');
        while (data.length) {
          tmp[tmp.length] = dV(data.splice(0, chunkSize).join(''));
        }

        return decompress(tmp);
      }
    };

  })();

  //------------------------------------------------------------------- /Main
  //creates a function that runs a callback for every node, recursively, and provides identification

  function makeRecursiveTraverser(callback) {
    var action = callback,
    cV = M.makeConverter(2);

    function recur(e, n, k, u) {
      var x, id = n + '' + cV(k);
      //doing stuff
      action(e, id);

      //go deeper
      k = 0;
      x = e.firstChild;
      for (; x; x = x.nextSibling) {
        if (x !== u && x.nodeType === 1) {
          recur(x, id, ++k);
        }
      }
    }
    return recur;
  }

  function rip(node,preprocess) {
    var htmlContent, rippedData, tmpdom, scripts;

    tmpdom = document.createElement(node.nodeName);
    tmpdom.innerHTML = node.innerHTML;
     if( typeof(preprocess) == 'function' ){
            preprocess(tmpdom);
        }

   if(S.keepJS){
        //no need for style and class
        htmlContent = tmpdom.innerHTML.replace(/(style)=("[^"<]*")|('[^'<]*')/gi, '')
    }else{
        scripts = tmpdom.getElementsByTagName('script');
        var i = scripts.length;
        while (i--) {
          scripts[i].parentNode.removeChild(scripts[i]);
        }
        //no need for style and class, drop events
        htmlContent = tmpdom.innerHTML.replace(/(style|on[^ =]*)=("[^"<]*")|('[^'<]*')/gi, '');
    }

    //; ignore whitespace
    htmlContent = htmlContent.replace(/\s+/g, ' ');

    rippedData = {
      html: htmlContent,
      css: {}
    };
    //store CSS recursively
    makeRecursiveTraverser(function(e, id) {
      rippedData.css[id] = CSS.get(e);
    })(node, '', 1);

    var fragment = JSON.stringify(rippedData);
    //console.log(fragment);
    if (S.heuristic) {
      fragment = Heuristic.compress(fragment);
    }

    var compressed = LZW.compress(fragment, M.makeConverter(S.numberLength));

    return compressed;

  }

  function put(data,target) {
    var node,findTR,nodeName = 'div',
        obj = LZW.decompress(data, M.reverseConverter, S.numberLength);
    if (S.heuristic) {
      obj = Heuristic.decompress(obj);
    }
    obj = JSON.parse(obj);
    //if code starts from tr, it needs a table tag to work
    findTR = obj.html.substring(0,5);
    if(findTR.indexOf('<tr')>-1 || findTR.indexOf('<th')>-1 || findTR.indexOf('<td')>-1 ){
      nodeName = 'table';
    }

    node = document.createElement(nodeName);
    node.innerHTML = obj.html;
    //console.log(obj);
    //set css back, recursively
    makeRecursiveTraverser(function(e, id) {
      var i,css = obj.css[id];
      for (var p in css) {
        i=CSS.camelize(p);
        e.style[i] = CSS.prefixify(css[p]);
        //console.log(p,i,css[p],CSS.prefixify(css[p]));
        if(i==='float'){
            e.style['cssFloat'] = css[p];
        }
      }
    })(node, '', 1);

    if(target){
      target.appendChild(node);
    }
    return node;

  }

  return {
    copy: rip,
    paste: put,
    test: function(fragment) {
      var test = LZW.compress(fragment, M.makeConverter(S.numberLength));
      return [test, LZW.decompress(test, M.reverseConverter, S.numberLength)];
    }
  };

}

/*
init example:
var ripper=Ripper({
  dictionary: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!~*;:/$,"
});

*/
<|endoftext|>