<commit_before>var chai = require('chai');
var expect = chai.expect;

var promptOptionsModule = require('../bin/promptOptions');

chai.should();

describe('As a user of the promptOptions module', function() {

    it('should return an object', function() {
        var test = promptOptionsModule;

        expect(test).to.be.a('object');
    })

    describe('When using getNewCommandPromptOptions()', function() {

        it('should be a function', function() {
            var test = promptOptionsModule.getNewCommandPromptOptions;

            expect(test).to.be.a('function');
        })

        it('should return an array', function(done) {
            promptOptionsModule
                .getNewCommandPromptOptions()
                .then(function(promptOptions) {
                    expect(promptOptions instanceof Array).to.be.true;
                    done();
                })
        })

        it('should return an array that is not empty', function(done) {
            promptOptionsModule
                .getNewCommandPromptOptions()
                .then(function(promptOptions) {
                    expect(promptOptions.length).to.be.above(1);
                    done();
                })
        })

    })

})
<commit_msg>tests: update prompt options test to use setup function<commit_after>var chai = require('chai');
var expect = chai.expect;

var promptOptionsModule = require('../bin/promptOptions');

chai.should();

describe('As a user of the promptOptions module', function() {

    it('should return an object', function() {
        var test = promptOptionsModule;

        expect(test).to.be.a('object');
    })

    describe('When using getNewCommandPromptOptions()', function() {

        before(function() {
            promptOptionsModule.setup({
                silent: true
            })
        })

        it('should be a function', function() {
            var test = promptOptionsModule.getNewCommandPromptOptions;

            expect(test).to.be.a('function');
        })

        it('should return an array', function(done) {
            promptOptionsModule
                .getNewCommandPromptOptions()
                .then(function(promptOptions) {
                    expect(promptOptions instanceof Array).to.be.true;
                    done();
                })
        })

        it('should return an array that is not empty', function(done) {
            promptOptionsModule
                .getNewCommandPromptOptions()
                .then(function(promptOptions) {
                    expect(promptOptions.length).to.be.above(1);
                    done();
                })
        })

    })

})
<|endoftext|>


<commit_before>import { downloadVideo } from '../downloader/downloader';

const path = require('path');
const prettyBytes = require('pretty-bytes');
const { shell } = require('electron');

let $datatable;
let $tableParent;

export var init = function () {
  const $table = $('table');

  $tableParent = $table.parent();
  $tableParent.hide();

  $datatable = $('table').DataTable({
    info: false,
    paging: false,
    autoWidth: false,
  });

  $('.dataTables_scrollBody').css('max-height', 200);
};

export var downloadVideoAndAddRowToTable = function (link, onError, onVideoAddedToTable) {
  let $tr;
  let $status;

  downloadVideo(link, onStartDownloading, onProgress, onError, onEnd);

  function onStartDownloading(info) {
    $tr = $(videoToHTML(info));
    $status = $tr.find('td.status');

    $datatable.row.add($tr).draw(false);
    $tableParent.show();

    onVideoAddedToTable();
  }

  function onProgress(percent) {
    $status.text(Math.round(percent) + '%');

    $tr.css('background-size', percent + '% 1px');
  }

  function onEnd(destinationFilePath) {
    videoEnd($tr, destinationFilePath);
  }
};

function videoToHTML(video) {
    return '
' +
            '' + video.title + '	' +
            '' + video.uploader + '	' +
            '' + video.duration + '	' +
            '' + prettyBytes(video.size) + '	' +
            '0%	' +
            '' +
                '<button title="(Cancel the download and) delete this file" class="btn btn-danger btn-sm">' +
                    '' +
                '</button>' +
            '	' +
        '
';
}

function videoEnd($tr, destinationFilePath) {
  $tr.addClass('done');

  const $button = $('<button title="Open the folder containing this file" class="btn btn-secondary btn-sm">' +
      '' +
  '</button>').on('click', () => shell.showItemInFolder(destinationFilePath));
  $tr.find('td.actions').html($button);
}
<commit_msg>Don't need to store tr status<commit_after>import { downloadVideo } from '../downloader/downloader';

const path = require('path');
const prettyBytes = require('pretty-bytes');
const { shell } = require('electron');

let $datatable;
let $tableParent;

export var init = function () {
  const $table = $('table');

  $tableParent = $table.parent();
  $tableParent.hide();

  $datatable = $('table').DataTable({
    info: false,
    paging: false,
    autoWidth: false,
  });

  $('.dataTables_scrollBody').css('max-height', 200);
};

export var downloadVideoAndAddRowToTable = function (link, onError, onVideoAddedToTable) {
  let $tr;

  downloadVideo(link, onStartDownloading, onProgress, onError, onEnd);

  function onStartDownloading(info) {
    $tr = $(videoToHTML(info));

    $datatable.row.add($tr).draw(false);
    $tableParent.show();

    onVideoAddedToTable();
  }

  function onProgress(percent) {
    $tr.find('td.status').text(Math.round(percent) + '%');

    $tr.css('background-size', percent + '% 1px');
  }

  function onEnd(destinationFilePath) {
    videoEnd($tr, destinationFilePath);
  }
};

function videoToHTML(video) {
    return '
' +
            '' + video.title + '	' +
            '' + video.uploader + '	' +
            '' + video.duration + '	' +
            '' + prettyBytes(video.size) + '	' +
            '0%	' +
            '' +
                '<button title="(Cancel the download and) delete this file" class="btn btn-danger btn-sm">' +
                    '' +
                '</button>' +
            '	' +
        '
';
}

function videoEnd($tr, destinationFilePath) {
  $tr.addClass('done');

  const $button = $('<button title="Open the folder containing this file" class="btn btn-secondary btn-sm">' +
      '' +
  '</button>').on('click', () => shell.showItemInFolder(destinationFilePath));
  $tr.find('td.actions').html($button);
}
<|endoftext|>


<commit_before>"use strict";
var util = require("util");
var Promise = require("promise");
var assert = require("chai").assert;
var intercept = require("intercept-stdout");

var TDL = require("../..");

var testBroker = require("../test_broker");

const HOSTNAME = "localhost";
const PORT = 21613;
const REQUEST_QUEUE_NAME = "some-user-req";
const RESPONSE_QUEUE_NAME = "some-user-resp";

module.exports = function() {
  // ~~~~~ Setup
  // TODO: how do we implement this in NodeJS, like we did in Java, see https://github.com/julianghionoiu/tdl-client-java/commit/4475fc3b01bb3f6fbc2b2d423848f5dcec489461#diff-0672afec8f176ab43f4d558fe4023e5dR54
  // Cucumber does not support .And() clause in NodeJS/JS
  this.Given(
    /^I start with a clean broker having a request and a response queue$/,
    function(callback) {
      var world = this;

      testBroker
        .connect()
        .then(function(broker) {
          world.broker = broker;
          world.requestQueue = broker.addQueueAndPurge(REQUEST_QUEUE_NAME);
          world.responseQueue = broker.addQueueAndPurge(RESPONSE_QUEUE_NAME);
        })
        .then(proceed(callback), orReportException(callback));
    }
  );

  this.Given(/^a client that connects to the queues$/, function(callback) {
    var world = this;

    testBroker
      .connect()
      .then(function(queues = [world.requestQueue, world.responseQueue]) {
        console.log("Saving queues: " + queues);

        var runnerConfig = new TDL.ImplementationRunnerConfig()
          .setHostname(HOSTNAME)
          .setPort(PORT)
          .setRequestQueueName(REQUEST_QUEUE_NAME)
          .setResponseQueueName(RESPONSE_QUEUE_NAME);

        world.runnerBuilder = new TDL.QueryBasedImplementationRunnerBuilder().setConfig(
          runnerConfig
        );

        world.runner = world.runnerBuilder.create();
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Given(/^the broker is not available$/, function(callback) {
    var world = this;

    var runnerConfig = new TDL.ImplementationRunnerConfig()
      .setHostname("111")
      .setPort(PORT)
      .setRequestQueueName("x")
      .setResponseQueueName("y");

    world.runnerBuilder = new TDL.QueryBasedImplementationRunnerBuilder().setConfig(
      runnerConfig
    );

    callback();
  });

  this.Given(/^I receive the following requests:$/, function(table, callback) {
    var world = this;
    console.log("table: " + util.inspect(table.hashes()));
    world.requestCount = table.hashes().length;

    // Send messages sequentially
    var sendAllMessages = table.hashes().reduce(function(p, row) {
      console.log("Send: " + row);
      return p.then(function() {
        return world.requestQueue.sendTextMessage(row["payload"]);
      });
    }, Promise.resolve());

    sendAllMessages.then(proceed(callback), orReportException(callback));
  });

  this.Given(/^I receive (\d+) identical requests like:$/, function(
    repeatCount,
    table,
    callback
  ) {
    var world = this;
    console.log("repeat count: " + repeatCount);
    console.log("table: " + util.inspect(table.hashes()));

    var repeatedRequests = [].concat.apply(
      [],
      Array(+repeatCount).fill(table.hashes())
    );
    world.requestCount = repeatedRequests.length;

    var sendAllMessages = repeatedRequests.reduce(function(p, row) {
      console.log("Send: " + util.inspect(row));
      return p.then(function() {
        return world.requestQueue.sendTextMessage(row["payload"]);
      });
    }, Promise.resolve());

    sendAllMessages.then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the time to wait for requests is (\d+)ms$/, function(
    expectedTimeout,
    callback
  ) {
    var world = this;
    var actualTimeout = world.runner.getRequestTimeoutMillisecond();
    assert.equal(
      actualTimeout,
      expectedTimeout,
      "The client request timeout has a different value."
    );
    callback();
  });

  this.Then(/^the request queue is "([^"]*)"$/, function(
    expectedValue,
    callback
  ) {
    var world = this;
    world.requestQueue
      .getName()
      .then(function(name) {
        assert.equal(
          name,
          expectedValue,
          "Request queue has a different value."
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the response queue is "([^"]*)"$/, function(
    expectedValue,
    callback
  ) {
    var world = this;
    world.responseQueue
      .getName()
      .then(function(name) {
        assert.equal(
          name,
          expectedValue,
          "Response queue has a different value."
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  // ~~~~~ Implementations

  function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
      if (new Date().getTime() - start > milliseconds) {
        break;
      }
    }
  }

  var USER_IMPLEMENTATIONS = {
    "add two numbers": function(x, y) {
      return x + y;
    },
    "return null": function() {
      return null;
    },
    "throw exception": function() {
      throw new Error();
    },
    "some logic": function(value) {
      return value;
    },
    "increment number": function(x) {
      return x + 1;
    },
    "echo the request": function(x) {
      return x;
    },
    "work for 600ms": function() {
      sleep(600);
      return "OK";
    }
  };

  function asImplementation(call) {
    if (call in USER_IMPLEMENTATIONS) {
      return USER_IMPLEMENTATIONS[call];
    } else {
      throw Error("Not a valid implementation reference: " + call);
    }
  }

  this.When(/^I go live with the following processing rules:$/, function(
    table,
    callback
  ) {
    var world = this;

    table.hashes().forEach(function(rule) {
      world.runnerBuilder.withSolutionFor(
        rule.method,
        asImplementation(rule.call)
      );
    });

    world.runner = world.runnerBuilder.create();

    //Setup log capture then run
    startIntercept(world);
    var logThenCallback = function() {
      endIntercept(world);
      callback();
    };

    var timestampBefore = new Date();
    world.runner
      .run()
      .then(proceed(logThenCallback), orReportException(logThenCallback))
      .then(function() {
        var timestampAfter = new Date();
        world.processingTime = timestampAfter - timestampBefore;
        console.log("Processing time: %dms", world.processingTime);
      });
  });

  // ~~~~~ Assertions

  this.Then(/^the client should consume all requests$/, function(callback) {
    var world = this;
    world.requestQueue
      .getSize()
      .then(function(size) {
        assert.equal(size, 0, "Requests have not been consumed");
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should consume first request$/, function(callback) {
    var world = this;
    world.requestQueue
      .getSize()
      .then(function(size) {
        assert.equal(
          size,
          world.requestCount - 1,
          "Requests have not been consumed"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should publish the following responses:$/, function(
    table,
    callback
  ) {
    var world = this;
    var expectedMessages = table.hashes().map(function(row) {
      return row["payload"];
    });

    world.responseQueue
      .getMessageContents()
      .then(function(receivedMessages) {
        assert.deepEqual(
          receivedMessages,
          expectedMessages,
          "The responses are not correct"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should display to console:$/, function(
    table,
    callback
  ) {
    var world = this;
    Promise.resolve(world.capturedText)
      .then(function(capturedText) {
        table.hashes().forEach(function(row) {
          assert.include(capturedText, row["output"]);
        });
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should not consume any request$/, function(callback) {
    var world = this;
    world.requestQueue
      .getSize()
      .then(function(size) {
        assert.equal(
          size,
          world.requestCount,
          "The request queue has different size. Messages have been consumed"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should not publish any response$/, function(callback) {
    var world = this;
    world.responseQueue
      .getSize()
      .then(function(size) {
        assert.equal(
          size,
          0,
          "The response queue has different size. Messages have been published"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^I should get no exception$/, function(callback) {
    //if you get here there were no exceptions
    callback();
  });

  this.Then(/^the processing time should be lower than (\d+)ms$/, function(
    threshold,
    callback
  ) {
    var world = this;
    assert.isBelow(world.processingTime, +threshold);
    callback();
  });
};

// ~~~~~ Helpers

function startIntercept(world) {
  world.capturedText = "";
  world.unhookIntercept = intercept(function(txt) {
    world.capturedText += txt;
  });
}

function endIntercept(world) {
  world.unhookIntercept();
}

function proceed(callback) {
  return function() {
    console.log("All Good. Continue.");
    callback();
  };
}

function orReportException(callback) {
  return function(err) {
    console.log("Oops. Error.");
    if (err.constructor.name === "AssertionError") {
      console.log("Assertion failed with: " + err.message);
      console.log("Expected: " + err.expected);
      console.log("Actual:   " + err.actual);
    }
    callback(err);
  };
}
<commit_msg>Add two missing test cases to queue step (consume and publish requests/responses)<commit_after>"use strict";
var util = require("util");
var Promise = require("promise");
var assert = require("chai").assert;
var intercept = require("intercept-stdout");

var TDL = require("../..");

var testBroker = require("../test_broker");

const HOSTNAME = "localhost";
const PORT = 21613;
const REQUEST_QUEUE_NAME = "some-user-req";
const RESPONSE_QUEUE_NAME = "some-user-resp";

module.exports = function() {
  // ~~~~~ Setup
  // TODO: how do we implement this in NodeJS, like we did in Java, see https://github.com/julianghionoiu/tdl-client-java/commit/4475fc3b01bb3f6fbc2b2d423848f5dcec489461#diff-0672afec8f176ab43f4d558fe4023e5dR54
  // Cucumber does not support .And() clause in NodeJS/JS
  this.Given(
    /^I start with a clean broker having a request and a response queue$/,
    function(callback) {
      var world = this;

      testBroker
        .connect()
        .then(function(broker) {
          world.broker = broker;
          world.requestQueue = broker.addQueueAndPurge(REQUEST_QUEUE_NAME);
          world.responseQueue = broker.addQueueAndPurge(RESPONSE_QUEUE_NAME);
        })
        .then(proceed(callback), orReportException(callback));
    }
  );

  this.Given(/^a client that connects to the queues$/, function(callback) {
    var world = this;

    testBroker
      .connect()
      .then(function(queues = [world.requestQueue, world.responseQueue]) {
        console.log("Saving queues: " + queues);

        var runnerConfig = new TDL.ImplementationRunnerConfig()
          .setHostname(HOSTNAME)
          .setPort(PORT)
          .setRequestQueueName(REQUEST_QUEUE_NAME)
          .setResponseQueueName(RESPONSE_QUEUE_NAME);

        world.runnerBuilder = new TDL.QueryBasedImplementationRunnerBuilder().setConfig(
          runnerConfig
        );

        world.runner = world.runnerBuilder.create();
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Given(/^the broker is not available$/, function(callback) {
    var world = this;

    var runnerConfig = new TDL.ImplementationRunnerConfig()
      .setHostname("111")
      .setPort(PORT)
      .setRequestQueueName("x")
      .setResponseQueueName("y");

    world.runnerBuilder = new TDL.QueryBasedImplementationRunnerBuilder().setConfig(
      runnerConfig
    );

    callback();
  });

  this.Given(/^I receive the following requests:$/, function(table, callback) {
    var world = this;
    console.log("table: " + util.inspect(table.hashes()));
    world.requestCount = table.hashes().length;

    // Send messages sequentially
    var sendAllMessages = table.hashes().reduce(function(p, row) {
      console.log("Send: " + row);
      return p.then(function() {
        return world.requestQueue.sendTextMessage(row["payload"]);
      });
    }, Promise.resolve());

    sendAllMessages.then(proceed(callback), orReportException(callback));
  });

  this.Given(/^I receive (\d+) identical requests like:$/, function(
    repeatCount,
    table,
    callback
  ) {
    var world = this;
    console.log("repeat count: " + repeatCount);
    console.log("table: " + util.inspect(table.hashes()));

    var repeatedRequests = [].concat.apply(
      [],
      Array(+repeatCount).fill(table.hashes())
    );
    world.requestCount = repeatedRequests.length;

    var sendAllMessages = repeatedRequests.reduce(function(p, row) {
      console.log("Send: " + util.inspect(row));
      return p.then(function() {
        return world.requestQueue.sendTextMessage(row["payload"]);
      });
    }, Promise.resolve());

    sendAllMessages.then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the time to wait for requests is (\d+)ms$/, function(
    expectedTimeout,
    callback
  ) {
    var world = this;
    var actualTimeout = world.runner.getRequestTimeoutMillisecond();
    assert.equal(
      actualTimeout,
      expectedTimeout,
      "The client request timeout has a different value."
    );
    callback();
  });

  this.Then(/^the request queue is "([^"]*)"$/, function(
    expectedValue,
    callback
  ) {
    var world = this;
    world.requestQueue
      .getName()
      .then(function(name) {
        assert.equal(
          name,
          expectedValue,
          "Request queue has a different value."
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the response queue is "([^"]*)"$/, function(
    expectedValue,
    callback
  ) {
    var world = this;
    world.responseQueue
      .getName()
      .then(function(name) {
        assert.equal(
          name,
          expectedValue,
          "Response queue has a different value."
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should consume one request$/, function(
      expectedValue = 1,
      callback
  ) {
      var world = this;
      world.requestQueue
          .getName()
          .then(function(name) {
              assert.equal(
                  name,
                  expectedValue,
                  "Request queue has a different value."
              );
          })
          .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should publish one response$/, function(
      expectedValue = 1,
      callback
  ) {
      var world = this;
      world.responseQueue
          .getName()
          .then(function(name) {
              assert.equal(
                  name,
                  expectedValue,
                  "Response queue has a different value."
              );
          })
          .then(proceed(callback), orReportException(callback));
  });

  // ~~~~~ Implementations

  function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
      if (new Date().getTime() - start > milliseconds) {
        break;
      }
    }
  }

  var USER_IMPLEMENTATIONS = {
    "add two numbers": function(x, y) {
      return x + y;
    },
    "return null": function() {
      return null;
    },
    "throw exception": function() {
      throw new Error();
    },
    "some logic": function(value) {
      return value;
    },
    "increment number": function(x) {
      return x + 1;
    },
    "echo the request": function(x) {
      return x;
    },
    "work for 600ms": function() {
      sleep(600);
      return "OK";
    }
  };

  function asImplementation(call) {
    if (call in USER_IMPLEMENTATIONS) {
      return USER_IMPLEMENTATIONS[call];
    } else {
      throw Error("Not a valid implementation reference: " + call);
    }
  }

  this.When(/^I go live with the following processing rules:$/, function(
    table,
    callback
  ) {
    var world = this;

    table.hashes().forEach(function(rule) {
      world.runnerBuilder.withSolutionFor(
        rule.method,
        asImplementation(rule.call)
      );
    });

    world.runner = world.runnerBuilder.create();

    //Setup log capture then run
    startIntercept(world);
    var logThenCallback = function() {
      endIntercept(world);
      callback();
    };

    var timestampBefore = new Date();
    world.runner
      .run()
      .then(proceed(logThenCallback), orReportException(logThenCallback))
      .then(function() {
        var timestampAfter = new Date();
        world.processingTime = timestampAfter - timestampBefore;
        console.log("Processing time: %dms", world.processingTime);
      });
  });

  // ~~~~~ Assertions

  this.Then(/^the client should consume all requests$/, function(callback) {
    var world = this;
    world.requestQueue
      .getSize()
      .then(function(size) {
        assert.equal(size, 0, "Requests have not been consumed");
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should consume first request$/, function(callback) {
    var world = this;
    world.requestQueue
      .getSize()
      .then(function(size) {
        assert.equal(
          size,
          world.requestCount - 1,
          "Requests have not been consumed"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should publish the following responses:$/, function(
    table,
    callback
  ) {
    var world = this;
    var expectedMessages = table.hashes().map(function(row) {
      return row["payload"];
    });

    world.responseQueue
      .getMessageContents()
      .then(function(receivedMessages) {
        assert.deepEqual(
          receivedMessages,
          expectedMessages,
          "The responses are not correct"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should display to console:$/, function(
    table,
    callback
  ) {
    var world = this;
    Promise.resolve(world.capturedText)
      .then(function(capturedText) {
        table.hashes().forEach(function(row) {
          assert.include(capturedText, row["output"]);
        });
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should not consume any request$/, function(callback) {
    var world = this;
    world.requestQueue
      .getSize()
      .then(function(size) {
        assert.equal(
          size,
          world.requestCount,
          "The request queue has different size. Messages have been consumed"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^the client should not publish any response$/, function(callback) {
    var world = this;
    world.responseQueue
      .getSize()
      .then(function(size) {
        assert.equal(
          size,
          0,
          "The response queue has different size. Messages have been published"
        );
      })
      .then(proceed(callback), orReportException(callback));
  });

  this.Then(/^I should get no exception$/, function(callback) {
    //if you get here there were no exceptions
    callback();
  });

  this.Then(/^the processing time should be lower than (\d+)ms$/, function(
    threshold,
    callback
  ) {
    var world = this;
    assert.isBelow(world.processingTime, +threshold);
    callback();
  });
};

// ~~~~~ Helpers

function startIntercept(world) {
  world.capturedText = "";
  world.unhookIntercept = intercept(function(txt) {
    world.capturedText += txt;
  });
}

function endIntercept(world) {
  world.unhookIntercept();
}

function proceed(callback) {
  return function() {
    console.log("All Good. Continue.");
    callback();
  };
}

function orReportException(callback) {
  return function(err) {
    console.log("Oops. Error.");
    if (err.constructor.name === "AssertionError") {
      console.log("Assertion failed with: " + err.message);
      console.log("Expected: " + err.expected);
      console.log("Actual:   " + err.actual);
    }
    callback(err);
  };
}
<|endoftext|>


<commit_before>define('table2chart', function () {
    "use strict";

    var me = {};

    me.converters = {
        // TODO handle i18n conversions
        "number": function (val) {
            return parseFloat(val);
        },
        "string": function (val) {
            return val;
        },
        "date": function (val) {
            var d = me.converters.datetime(val);
            // only keep date
            return new Date(d.getFullYear(), d.getMonth(), d.getDate());

        },
        "datetime": function (val) {
            return new Date(val);

        },
        "timeofday": function (val) {
            var m = val.match(/^(\d{1,2}):(\d{1,2}):(\d{1,2})(\.(\d{1,3}))?$/);
            if (m.length !== 5 && m.length !== 6) {
                throw Error('Bad timeofday value: ' + val);
            }
            var retval = [m[1], m[2], m[3]];
            if (m.length > 5 && m[5]) retval.push(m[5]);
            return retval;

        },
        "boolean": function (val) {
            if (val &&
                (val.toLowerCase() === 'true' ||
                    val.toLowerCase() === 'yes' ||
                    val.toLowerCase() === 'on')) {
                return true;
            }
            return false;
        }
    };

    // TODO replace the 'ignore' datatype with an option on the placeholder
    me.DATA_TYPE_IGNORE = 'ignore';

    function tableDomAdapter(table) {

        if (table.tagName.toLowerCase() !== 'table') {
            throw Error('Data source must be a table');
        }

        function getTableComponent(table) {
            var tableComponents = {};
            for (var i = 0; i < table.children.length; i++) {
                var child = table.children[i];
                if (child.tagName.toLowerCase() === 'caption') tableComponents.caption = child;
                if (child.tagName.toLowerCase() === 'tbody') tableComponents.tbody = child;
                if (child.tagName.toLowerCase() === 'thead') tableComponents.thead = child;
            }

            if (!tableComponents.tbody) throw Error('table has no "tbody" child');
            if (!tableComponents.thead) throw Error('table has no "thead" child');
            if (!tableComponents.caption) console.log('table has no "caption" child');

            return tableComponents;
        }

        return {
            tableComponents: getTableComponent(table),

            getCaption: function () {
                if (this.tableComponents.caption) return this.tableComponents.caption.textContent;
                return null;
            },
            __getHeaderLine: function () {
                return this.tableComponents.thead.children[0];
            },
            __getLine: function (index) {
                return this.tableComponents.tbody.children[index];
            },
            getColumnCount: function () {
                return this.__getHeaderLine().childElementCount;
            },
            getLineCount: function () {
                return this.tableComponents.tbody.childElementCount;
            },
            getHeaderCell: function (index) {
                var cell = {};

                var domCell = this.__getHeaderLine().children[index];

                cell.label = domCell.textContent;

                cell.dataType = domCell.getAttribute('data-t2c-type');

                if (!cell.dataType) {
                    if (index === 0) cell.dataType = 'string';
                    else cell.dataType = 'number';
                }

                if (cell.dataType && me.converters[cell.dataType]) {
                    cell.converter = me.converters[cell.dataType];
                } else {
                    cell.converter = null;
                }

                return cell;
            },
            getDataCell: function (lineIndex, columnIndex) {
                var header = this.getHeaderCell(columnIndex);
                var line = this.__getLine(lineIndex);
                var td = line.children[columnIndex];
                var retval = td.textContent;
                if (header.converter) {
                    retval = header.converter(retval);
                }
                return retval;
            },

            toGoogleDataTable: function () {
                var googleDataTable = new google.visualization.DataTable();

                var converters = [];

                var columnIndex;

                for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                    var cell = this.getHeaderCell(columnIndex);
                    if (cell.dataType !== me.DATA_TYPE_IGNORE) {
                        googleDataTable.addColumn(cell.dataType, cell.label);
                    }
                }

                for (var lineIndex = 0; lineIndex < this.getLineCount(); lineIndex++) {

                    var googleDataLine = [];

                    for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                        var headerCell = this.getHeaderCell(columnIndex);
                        if (headerCell.dataType !== me.DATA_TYPE_IGNORE) {
                            var cellData = this.getDataCell(lineIndex, columnIndex);
                            googleDataLine.push(cellData);
                        }

                    }

                    googleDataTable.addRows([googleDataLine]);
                }

                return googleDataTable;
            }
        };
    }

    function placeholderDomAdapter(placeholder) {
        return {
            applySize: function () {
                var sizeAsString = placeholder.getAttribute('data-t2c-size');
                if (sizeAsString) {
                    var dim = sizeAsString.split('x');
                    if (dim && dim.length === 2) {
                        placeholder.style.width = dim[0];
                        placeholder.style.height = dim[1];
                    }
                }
            },

            getGoogleOptions: function (tableAdapter) {

                var options = {};

                var optAsString = placeholder.getAttribute('data-t2c-options');
                //console.log(optAsString);
                if (optAsString) {
                    try {
                        options = JSON.parse(optAsString);
                    } catch (err) {
                        throw Error('Invalid JSON options: ' + optAsString);
                    }
                }

                if (!options.title) {
                    var caption = tableAdapter.getCaption();
                    if (caption) options.title = caption;
                }

                return options;
            },

            getTableAdapter: function (table) {
                if (!table) table = placeholder;

                var tableId = placeholder.getAttribute('data-t2c-source');

                if (tableId) table = document.getElementById(tableId);

                var tableAdapter = tableDomAdapter(table);

                return tableAdapter;
            },

            drawGoogleChart: function (table) {
                var kindId = placeholder.getAttribute('data-t2c');

                if (!google.visualization[kindId]) {
                    throw Error('Unknown Chart kind: ' + kindId);
                }

                var tableAdapter = this.getTableAdapter(table);

                var data = tableAdapter.toGoogleDataTable();

                this.applySize();

                var options = this.getGoogleOptions(tableAdapter);

                var wrapper = new google.visualization.ChartWrapper({
                    'chartType': kindId,
                    'dataTable': data,
                    'options': options,
                    'containerId': placeholder
                });
                wrapper.draw();

                return wrapper;
            }
        };
    }

    me.drawGoogleChart = function (placeholder, table) {

        var placeholderAdapter = placeholderDomAdapter(placeholder);

        return placeholderAdapter.drawGoogleChart(table);
    };

    return me;
});
<commit_msg>Removed unused var<commit_after>define('table2chart', function () {
    "use strict";

    var me = {};

    me.converters = {
        // TODO handle i18n conversions
        "number": function (val) {
            return parseFloat(val);
        },
        "string": function (val) {
            return val;
        },
        "date": function (val) {
            var d = me.converters.datetime(val);
            // only keep date
            return new Date(d.getFullYear(), d.getMonth(), d.getDate());

        },
        "datetime": function (val) {
            return new Date(val);

        },
        "timeofday": function (val) {
            var m = val.match(/^(\d{1,2}):(\d{1,2}):(\d{1,2})(\.(\d{1,3}))?$/);
            if (m.length !== 5 && m.length !== 6) {
                throw Error('Bad timeofday value: ' + val);
            }
            var retval = [m[1], m[2], m[3]];
            if (m.length > 5 && m[5]) retval.push(m[5]);
            return retval;

        },
        "boolean": function (val) {
            if (val &&
                (val.toLowerCase() === 'true' ||
                    val.toLowerCase() === 'yes' ||
                    val.toLowerCase() === 'on')) {
                return true;
            }
            return false;
        }
    };

    // TODO replace the 'ignore' datatype with an option on the placeholder
    me.DATA_TYPE_IGNORE = 'ignore';

    function tableDomAdapter(table) {

        if (table.tagName.toLowerCase() !== 'table') {
            throw Error('Data source must be a table');
        }

        function getTableComponent(table) {
            var tableComponents = {};
            for (var i = 0; i < table.children.length; i++) {
                var child = table.children[i];
                if (child.tagName.toLowerCase() === 'caption') tableComponents.caption = child;
                if (child.tagName.toLowerCase() === 'tbody') tableComponents.tbody = child;
                if (child.tagName.toLowerCase() === 'thead') tableComponents.thead = child;
            }

            if (!tableComponents.tbody) throw Error('table has no "tbody" child');
            if (!tableComponents.thead) throw Error('table has no "thead" child');
            if (!tableComponents.caption) console.log('table has no "caption" child');

            return tableComponents;
        }

        return {
            tableComponents: getTableComponent(table),

            getCaption: function () {
                if (this.tableComponents.caption) return this.tableComponents.caption.textContent;
                return null;
            },
            __getHeaderLine: function () {
                return this.tableComponents.thead.children[0];
            },
            __getLine: function (index) {
                return this.tableComponents.tbody.children[index];
            },
            getColumnCount: function () {
                return this.__getHeaderLine().childElementCount;
            },
            getLineCount: function () {
                return this.tableComponents.tbody.childElementCount;
            },
            getHeaderCell: function (index) {
                var cell = {};

                var domCell = this.__getHeaderLine().children[index];

                cell.label = domCell.textContent;

                cell.dataType = domCell.getAttribute('data-t2c-type');

                if (!cell.dataType) {
                    if (index === 0) cell.dataType = 'string';
                    else cell.dataType = 'number';
                }

                if (cell.dataType && me.converters[cell.dataType]) {
                    cell.converter = me.converters[cell.dataType];
                } else {
                    cell.converter = null;
                }

                return cell;
            },
            getDataCell: function (lineIndex, columnIndex) {
                var header = this.getHeaderCell(columnIndex);
                var line = this.__getLine(lineIndex);
                var td = line.children[columnIndex];
                var retval = td.textContent;
                if (header.converter) {
                    retval = header.converter(retval);
                }
                return retval;
            },

            toGoogleDataTable: function () {
                var googleDataTable = new google.visualization.DataTable();

                var columnIndex;

                for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                    var cell = this.getHeaderCell(columnIndex);
                    if (cell.dataType !== me.DATA_TYPE_IGNORE) {
                        googleDataTable.addColumn(cell.dataType, cell.label);
                    }
                }

                for (var lineIndex = 0; lineIndex < this.getLineCount(); lineIndex++) {

                    var googleDataLine = [];

                    for (columnIndex = 0; columnIndex < this.getColumnCount(); columnIndex++) {

                        var headerCell = this.getHeaderCell(columnIndex);
                        if (headerCell.dataType !== me.DATA_TYPE_IGNORE) {
                            var cellData = this.getDataCell(lineIndex, columnIndex);
                            googleDataLine.push(cellData);
                        }

                    }

                    googleDataTable.addRows([googleDataLine]);
                }

                return googleDataTable;
            }
        };
    }

    function placeholderDomAdapter(placeholder) {
        return {
            applySize: function () {
                var sizeAsString = placeholder.getAttribute('data-t2c-size');
                if (sizeAsString) {
                    var dim = sizeAsString.split('x');
                    if (dim && dim.length === 2) {
                        placeholder.style.width = dim[0];
                        placeholder.style.height = dim[1];
                    }
                }
            },

            getGoogleOptions: function (tableAdapter) {

                var options = {};

                var optAsString = placeholder.getAttribute('data-t2c-options');
                //console.log(optAsString);
                if (optAsString) {
                    try {
                        options = JSON.parse(optAsString);
                    } catch (err) {
                        throw Error('Invalid JSON options: ' + optAsString);
                    }
                }

                if (!options.title) {
                    var caption = tableAdapter.getCaption();
                    if (caption) options.title = caption;
                }

                return options;
            },

            getTableAdapter: function (table) {
                if (!table) table = placeholder;

                var tableId = placeholder.getAttribute('data-t2c-source');

                if (tableId) table = document.getElementById(tableId);

                var tableAdapter = tableDomAdapter(table);

                return tableAdapter;
            },

            drawGoogleChart: function (table) {
                var kindId = placeholder.getAttribute('data-t2c');

                if (!google.visualization[kindId]) {
                    throw Error('Unknown Chart kind: ' + kindId);
                }

                var tableAdapter = this.getTableAdapter(table);

                var data = tableAdapter.toGoogleDataTable();

                this.applySize();

                var options = this.getGoogleOptions(tableAdapter);

                var wrapper = new google.visualization.ChartWrapper({
                    'chartType': kindId,
                    'dataTable': data,
                    'options': options,
                    'containerId': placeholder
                });
                wrapper.draw();

                return wrapper;
            }
        };
    }

    me.drawGoogleChart = function (placeholder, table) {

        var placeholderAdapter = placeholderDomAdapter(placeholder);

        return placeholderAdapter.drawGoogleChart(table);
    };

    return me;
});
<|endoftext|>


<commit_before>/*
Unlike promised, even with shallow rendering we need the dom sometime in React 0.13...
*/
var jsdom = require("jsdom");

global.document = jsdom.jsdom('<!doctype html>');
global.window = document.parentWindow;
global.navigator = window.navigator;

var mobservable = require('mobservable');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var observer = require('mobservable-react').observer;

var e = React.createElement;

var store = mobservable.observable({
    todos: [{
        title: "a",
        completed: false
    }]
});

var todoItemRenderings = 0;
var todoItem = observer(function TodoItem(props) {
    todoItemRenderings++;
    return e("li", {}, props.todo.title);
});

var todoListRenderings = 0;
var todoList = observer(React.createClass({
    renderings: 0,
    render: function() {
        todoListRenderings++;
        var todos = this.props.store;
        return e("div", {},
            e("h1", null, todos.todos.length),
            todos.todos.map(function(todo, idx) {
                return e(todoItem, {
                    key: idx,
                    todo: todo
                });
            })
        );
    }
}));

var app = React.createClass({
    render: function() {
        return e(todoList);
    }
});

function shallow(component, props) {
    var renderer = TestUtils.createRenderer();
    renderer.render(e(component, props));
    return renderer;
}

exports.testNestedRendering = function(test) {
    var list$ = shallow(todoList, { store: store });
    var listResult = list$.getRenderOutput();

    test.equal(todoListRenderings, 1, "should have rendered list once");
    test.equal(listResult.props.children.length, 2, "list should have two children");
    test.equal(listResult.props.children[0].props.children, "1", "should have correct title");
    test.equal(listResult.props.children[1].length, 1, "list should on item in the list");

    var item1$ = shallow(todoItem, listResult.props.children[1][0].props);
    item1$.getRenderOutput();
    test.equal(todoItemRenderings, 1, "item1 should render once");
    test.equal(mobservable.extras.getDNode(store, "todos").observers.length, 1, "observer count on store is not increased");
    test.equal(mobservable.extras.getDNode(store.todos[0], "title").observers.length, 1, "title observers should have increased");

    store.todos[0].title += "a";
    test.equal(todoListRenderings, 1, "should have rendered list once");
    test.equal(todoItemRenderings, 2, "item1 should have rendered twice");
    test.equal(mobservable.extras.getDNode(store, "todos").observers.length, 1, "observers count shouldn't change");
    test.equal(mobservable.extras.getDNode(store.todos[0], "title").observers.length, 1, "title observers should not have increased");

    store.todos.push({
        title: "b",
        completed: true
    });

    listResult = list$.getRenderOutput();
    test.equal(listResult.props.children[1].length, 2, "list should two items in in the list");

    var item2$ = shallow(todoItem, listResult.props.children[1][1].props);
    item2$.getRenderOutput();

    test.equal(todoListRenderings, 2, "should have rendered list twice");
    test.equal(todoItemRenderings, 3, "item2 should have rendered as well");
    test.equal(mobservable.extras.getDNode(store.todos[1], "title").observers.length, 1, "title observers should have increased");
    test.equal(mobservable.extras.getDNode(store.todos[1], "completed").observers.length, 0, "completed observers should not have increased");

    var oldTodo = store.todos.pop();
    listResult = list$.getRenderOutput();
    test.equal(todoListRenderings, 3, "should have rendered list another time");
    test.equal(todoItemRenderings, 3, "item1 should not have rerendered");
    test.equal(listResult.props.children[1].length, 1, "list should have only on item in list now");

    item2$.unmount(); // that's what react does normally for us..
    test.equal(mobservable.extras.getDNode(oldTodo, "title").observers.length, 0, "title observers should have decreased");
    test.equal(mobservable.extras.getDNode(oldTodo, "completed").observers.length, 0, "completed observers should not have decreased");

    test.done();
}

exports.testIsComponentReactive = function(test) {
    var component = observer({ render: function() {}});
    test.equal(component.isMobservableReactObserver, true);
    test.equal(mobservable.isObservable(component), false); // dependencies not known yet
    test.equal(mobservable.isObservable(component.render), false); // dependencies not known yet

    component.componentWillMount();
    component.render();
    test.equal(mobservable.isObservable(component.render), true); // dependencies not known yet
    test.equal(mobservable.isObservable(component), false);

    mobservable.extendReactive(component, {});
    test.equal(mobservable.isObservable(component), true);

    test.done();
}

exports.testGetDNode = function(test) {
    var getD = mobservable.extras.getDNode;

    var c = observer({ render: function() {}});
    c.componentWillMount();
    c.render();
    test.ok(getD(c.render));

    test.done();
}

exports.testKeepViewsAlive = function(test) {
    var yCalcCount = 0;
    var data = mobservable.observable({
        x: 3,
        y: function() {
            yCalcCount++;
            return this.x * 2;
        },
        z: "hi"
    });

    var component = observer(function testComponent() {
        return React.createElement("div", {}, data.z + data.y);
    });

    var c$ = shallow(component, { });

    var cResult = c$.getRenderOutput();
    test.equal(yCalcCount, 1);
    test.equal(cResult.props.children, "hi6");

    data.z = "hello";
    // test: rerender should not need a recomputation of data.y because the subscription is kept alive

    test.equal(yCalcCount, 1);

    cResult = c$.getRenderOutput();
    test.equal(cResult.props.children, "hello6");
    test.equal(yCalcCount, 1);

    test.equal(mobservable.extras.getDNode(data, "y").observers.length, 1);
    c$.unmount();
    test.equal(mobservable.extras.getDNode(data, "y").observers.length, 0);

    test.done();
}<commit_msg>stateless component props assertions added<commit_after>/*
Unlike promised, even with shallow rendering we need the dom sometime in React 0.13...
*/
var jsdom = require("jsdom");

global.document = jsdom.jsdom('<!doctype html>');
global.window = document.parentWindow;
global.navigator = window.navigator;

var mobservable = require('mobservable');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
var observer = require('mobservable-react').observer;

var e = React.createElement;

var store = mobservable.observable({
    todos: [{
        title: "a",
        completed: false
    }]
});

var todoItemRenderings = 0;
var todoItem = observer(function TodoItem(props) {
    todoItemRenderings++;
    return e("li", {}, props.todo.title);
});

var todoListRenderings = 0;
var todoList = observer(React.createClass({
    renderings: 0,
    render: function() {
        todoListRenderings++;
        var todos = this.props.store;
        return e("div", {},
            e("h1", null, todos.todos.length),
            todos.todos.map(function(todo, idx) {
                return e(todoItem, {
                    key: idx,
                    todo: todo
                });
            })
        );
    }
}));

var app = React.createClass({
    render: function() {
        return e(todoList);
    }
});

function shallow(component, props) {
    var renderer = TestUtils.createRenderer();
    renderer.render(e(component, props));
    return renderer;
}

exports.testNestedRendering = function(test) {
    var list$ = shallow(todoList, { store: store });
    var listResult = list$.getRenderOutput();

    test.equal(todoListRenderings, 1, "should have rendered list once");
    test.equal(listResult.props.children.length, 2, "list should have two children");
    test.equal(listResult.props.children[0].props.children, "1", "should have correct title");
    test.equal(listResult.props.children[1].length, 1, "list should on item in the list");

    var item1$ = shallow(todoItem, listResult.props.children[1][0].props);
    item1$.getRenderOutput();
    test.equal(todoItemRenderings, 1, "item1 should render once");
    test.equal(mobservable.extras.getDNode(store, "todos").observers.length, 1, "observer count on store is not increased");
    test.equal(mobservable.extras.getDNode(store.todos[0], "title").observers.length, 1, "title observers should have increased");

    store.todos[0].title += "a";
    test.equal(todoListRenderings, 1, "should have rendered list once");
    test.equal(todoItemRenderings, 2, "item1 should have rendered twice");
    test.equal(mobservable.extras.getDNode(store, "todos").observers.length, 1, "observers count shouldn't change");
    test.equal(mobservable.extras.getDNode(store.todos[0], "title").observers.length, 1, "title observers should not have increased");

    store.todos.push({
        title: "b",
        completed: true
    });

    listResult = list$.getRenderOutput();
    test.equal(listResult.props.children[1].length, 2, "list should two items in in the list");

    var item2$ = shallow(todoItem, listResult.props.children[1][1].props);
    item2$.getRenderOutput();

    test.equal(todoListRenderings, 2, "should have rendered list twice");
    test.equal(todoItemRenderings, 3, "item2 should have rendered as well");
    test.equal(mobservable.extras.getDNode(store.todos[1], "title").observers.length, 1, "title observers should have increased");
    test.equal(mobservable.extras.getDNode(store.todos[1], "completed").observers.length, 0, "completed observers should not have increased");

    var oldTodo = store.todos.pop();
    listResult = list$.getRenderOutput();
    test.equal(todoListRenderings, 3, "should have rendered list another time");
    test.equal(todoItemRenderings, 3, "item1 should not have rerendered");
    test.equal(listResult.props.children[1].length, 1, "list should have only on item in list now");

    item2$.unmount(); // that's what react does normally for us..
    test.equal(mobservable.extras.getDNode(oldTodo, "title").observers.length, 0, "title observers should have decreased");
    test.equal(mobservable.extras.getDNode(oldTodo, "completed").observers.length, 0, "completed observers should not have decreased");

    test.done();
}

exports.testIsComponentReactive = function(test) {
    var component = observer({ render: function() {}});
    test.equal(component.isMobservableReactObserver, true);
    test.equal(mobservable.isObservable(component), false); // dependencies not known yet
    test.equal(mobservable.isObservable(component.render), false); // dependencies not known yet

    component.componentWillMount();
    component.render();
    test.equal(mobservable.isObservable(component.render), true); // dependencies not known yet
    test.equal(mobservable.isObservable(component), false);

    mobservable.extendReactive(component, {});
    test.equal(mobservable.isObservable(component), true);

    test.done();
}

exports.testGetDNode = function(test) {
    var getD = mobservable.extras.getDNode;

    var c = observer({ render: function() {}});
    c.componentWillMount();
    c.render();
    test.ok(getD(c.render));

    test.done();
}

exports.testKeepViewsAlive = function(test) {
    var yCalcCount = 0;
    var data = mobservable.observable({
        x: 3,
        y: function() {
            yCalcCount++;
            return this.x * 2;
        },
        z: "hi"
    });

    var component = observer(function testComponent() {
        return React.createElement("div", {}, data.z + data.y);
    });

    var c$ = shallow(component, { });

    var cResult = c$.getRenderOutput();
    test.equal(yCalcCount, 1);
    test.equal(cResult.props.children, "hi6");

    data.z = "hello";
    // test: rerender should not need a recomputation of data.y because the subscription is kept alive

    test.equal(yCalcCount, 1);

    cResult = c$.getRenderOutput();
    test.equal(cResult.props.children, "hello6");
    test.equal(yCalcCount, 1);

    test.equal(mobservable.extras.getDNode(data, "y").observers.length, 1);
    c$.unmount();
    test.equal(mobservable.extras.getDNode(data, "y").observers.length, 0);

    test.done();
}

var stateLessComp = function() {
    todoListRenderings++;
    var todos = this.props.store;
    return e("div", {},
        e("h1", null, todos.todos.length),
        todos.todos.map(function(todo, idx) {
            return e(todoItem, {
                key: idx,
                todo: todo
            });
        })
    );
}
stateLessComp.propTypes = {
  testProp: React.PropTypes.string
}
stateLessComp.defaultProps = {
  testProp: 'default value for prop testProp'
}

exports.testStatelessComponentProps = function(test) {
    var statelessCompObserver = observer(stateLessComp);
    test.equal(statelessCompObserver.getDefaultProps().testProp, 'default value for prop testProp', "default property value should be propagated");
    var originalConsoleError = console.error
    var beenWarned = false
    console.error = function() {
      beenWarned = true;
    }
    React.createElement(statelessCompObserver, {testProp: 10})
    console.error = originalConsoleError;
    test.equal(beenWarned, true, "an error should be logged with a property type warning")
    test.done()
}
<|endoftext|>


<commit_before>import React from 'react';
import cx from 'classnames';
import { pure, skinnable, props, t } from '../utils';
import { Table } from 'fixed-data-table-2';
import Column, { defaultColumns, updateColumns } from './Column';
import FlexView from 'react-flexview';

const { maybe } = t;

/** A table component based on fixed-data-table-2
 * @param data - data shown in the table
 * @param width - the desired width of the table. Unless autosize is false, this can be left undefined
 * @param height - the desired height of the table.  Unless autosize is false, this can be left undefined
 * @param rowHeight - height in pixel of every row
 * @param headerHeight - height in pixel of header
 * @param groupHeaderHeight - height in pixel of groupHeader
 * @param footerHeight - height in pixel of footer
 * @param onRowMouseEnter - callback to be called when mouse enters a row
 * @param onRowMouseLeave - callback to be called when mouse leaves a row
 * @param autosize - wheater the table should resize to fit the available space. Default true.
 * @param columnsOrder - an array containing the ordered list of column names, in the same order they should be rendered
 * @param onColumnsReorder - callback to be called when the order of columns changes after dragging an header in a new position
 * @param onColumnResize - callback to be called when a column is resized
 * @param children - table children (Column or ColumnGroup)
 * @param scrollLeft - value of horizontal scroll
 * @param scrollTop - value of vertical scroll
 * @param onScrollStart - callback to be called when scrolling starts
 * @param onScrollEnd - callback to be called when scrolling ends
 * @param selectedRows - the list of selected row ids
 * @param onRowsSelect - callback to be called when the currently selected rows change
 * @param selectionType - single = only one selected row at a time, multi = multiple selection, none = selection disabled
 * @param hoveredRowIndex - the id of the hovered row
 * @param onHoverRowChange - callback to be called when the hovered row changes
 * @param sortBy - id of the column according which the data should be ordered
 * @param sortDir - sorting direction
 * @param onSortChange - callback to be called when sorting change
 *
 * @param scrollToRow - Private
 * @param onRowClick - Private
 * @param rowClassNameGetter - Private
 * @param onColumnResizeEndCallback - Private
 * @param isColumnResizing - Private
 */
@skinnable()
@pure
@props({
  // public
  className: maybe(t.String),
  data: t.Array,
  width: t.Number,
  height: t.Number,
  rowHeight: maybe(t.Number),
  headerHeight: maybe(t.Number),
  groupHeaderHeight: maybe(t.Number),
  footerHeight: maybe(t.Number),
  onRowMouseEnter: maybe(t.Function),
  onRowMouseLeave: maybe(t.Function),
  scrollLeft: maybe(t.Integer),
  scrollTop: maybe(t.Integer),
  onScrollStart: maybe(t.Function),
  onScrollEnd: maybe(t.Function),
  children: t.ReactChildren,

  // private
  scrollToRow: maybe(t.Integer),
  onRowClick: maybe(t.Function),
  rowClassNameGetter: maybe(t.Function),
  onColumnResizeEndCallback: maybe(t.Function),
  isColumnResizing: maybe(t.Boolean)
})
export default class Tablo extends React.Component {

  static defaultProps = {
    rowHeight: 30,
    headerHeight: 40,
    groupHeaderHeight: 50,
    footerHeight: 0
  }

  getLocals({ data, children, ...tableProps }) {

    const columnsOrGroups = updateColumns(children || defaultColumns(data), ({ col }) => {
      return <Column {...{ key: col.props.name, ...col.props, data }} />;
    }).map((ch, key) => ch.type({ key, ...ch.props }));

    t.assert(
      columnsOrGroups.length === ([].concat(children) || Object.keys).length,
      'There are extraneous children in the Grid. One should use only Column or ColumnGroup'
    );

    const rowsCount = data.length;

    return {
      columnsOrGroups,
      rowsCount,
      ...tableProps
    };
  }

  template({ columnsOrGroups, className, ...tableProps }) {
    return (
      <FlexView column grow className={cx('tablo', className)}>
        <Table {...tableProps}>
          {columnsOrGroups}
        </Table>
      </FlexView>
    );
  }

}
<commit_msg>fix tablo assert on children<commit_after>import React from 'react';
import cx from 'classnames';
import { pure, skinnable, props, t } from '../utils';
import { Table } from 'fixed-data-table-2';
import Column, { defaultColumns, updateColumns } from './Column';
import FlexView from 'react-flexview';

const { maybe } = t;

/** A table component based on fixed-data-table-2
 * @param data - data shown in the table
 * @param width - the desired width of the table. Unless autosize is false, this can be left undefined
 * @param height - the desired height of the table.  Unless autosize is false, this can be left undefined
 * @param rowHeight - height in pixel of every row
 * @param headerHeight - height in pixel of header
 * @param groupHeaderHeight - height in pixel of groupHeader
 * @param footerHeight - height in pixel of footer
 * @param onRowMouseEnter - callback to be called when mouse enters a row
 * @param onRowMouseLeave - callback to be called when mouse leaves a row
 * @param autosize - wheater the table should resize to fit the available space. Default true.
 * @param columnsOrder - an array containing the ordered list of column names, in the same order they should be rendered
 * @param onColumnsReorder - callback to be called when the order of columns changes after dragging an header in a new position
 * @param onColumnResize - callback to be called when a column is resized
 * @param children - table children (Column or ColumnGroup)
 * @param scrollLeft - value of horizontal scroll
 * @param scrollTop - value of vertical scroll
 * @param onScrollStart - callback to be called when scrolling starts
 * @param onScrollEnd - callback to be called when scrolling ends
 * @param selectedRows - the list of selected row ids
 * @param onRowsSelect - callback to be called when the currently selected rows change
 * @param selectionType - single = only one selected row at a time, multi = multiple selection, none = selection disabled
 * @param hoveredRowIndex - the id of the hovered row
 * @param onHoverRowChange - callback to be called when the hovered row changes
 * @param sortBy - id of the column according which the data should be ordered
 * @param sortDir - sorting direction
 * @param onSortChange - callback to be called when sorting change
 *
 * @param scrollToRow - Private
 * @param onRowClick - Private
 * @param rowClassNameGetter - Private
 * @param onColumnResizeEndCallback - Private
 * @param isColumnResizing - Private
 */
@skinnable()
@pure
@props({
  // public
  className: maybe(t.String),
  data: t.Array,
  width: t.Number,
  height: t.Number,
  rowHeight: maybe(t.Number),
  headerHeight: maybe(t.Number),
  groupHeaderHeight: maybe(t.Number),
  footerHeight: maybe(t.Number),
  onRowMouseEnter: maybe(t.Function),
  onRowMouseLeave: maybe(t.Function),
  scrollLeft: maybe(t.Integer),
  scrollTop: maybe(t.Integer),
  onScrollStart: maybe(t.Function),
  onScrollEnd: maybe(t.Function),
  children: t.ReactChildren,

  // private
  scrollToRow: maybe(t.Integer),
  onRowClick: maybe(t.Function),
  rowClassNameGetter: maybe(t.Function),
  onColumnResizeEndCallback: maybe(t.Function),
  isColumnResizing: maybe(t.Boolean)
})
export default class Tablo extends React.Component {

  static defaultProps = {
    rowHeight: 30,
    headerHeight: 40,
    groupHeaderHeight: 50,
    footerHeight: 0
  }

  getLocals({ data, children, ...tableProps }) {

    const columnsOrGroups = updateColumns(children || defaultColumns(data), ({ col }) => {
      return <Column {...{ key: col.props.name, ...col.props, data }} />;
    }).map((ch, key) => ch.type({ key, ...ch.props }));

    t.assert(
      columnsOrGroups.length === [].concat(children || Object.keys(data[0])).length,
      'There are extraneous children in the Grid. One should use only Column or ColumnGroup'
    );

    const rowsCount = data.length;

    return {
      columnsOrGroups,
      rowsCount,
      ...tableProps
    };
  }

  template({ columnsOrGroups, className, ...tableProps }) {
    return (
      <FlexView column grow className={cx('tablo', className)}>
        <Table {...tableProps}>
          {columnsOrGroups}
        </Table>
      </FlexView>
    );
  }

}
<|endoftext|>


<commit_before>describe('ResumableFile functions', function() {

  /**
   * @type {Resumable}
   */
  var resumable;
  /**
   * @type {ResumableFile}
   */
  var file;

  beforeEach(function () {
    resumable = new Resumable({
    });
    file = new resumable.ResumableFile(resumable, {
      name: 'image.jpg',
      type: 'image/png'
    });
  });

  it('should get type', function() {
    expect(file.getType()).toBe('png');
    file.file.type = '';
    expect(file.getType()).toBe('');
  });

  it('should get extension', function() {
    expect(file.name).toBe('image.jpg');
    expect(file.getExtension()).toBe('jpg');
    file.name = '';
    expect(file.getExtension()).toBe('');
    file.name = 'image';
    expect(file.getExtension()).toBe('');
    file.name = '.dwq.dq.wd.qdw.E';
    expect(file.getExtension()).toBe('e');
  });
});<commit_msg>test: test update<commit_after>describe('ResumableFile functions', function() {

  /**
   * @type {Resumable}
   */
  var resumable;
  /**
   * @type {ResumableFile}
   */
  var file;

  beforeEach(function () {
    resumable = new Resumable({
    });
    file = new Resumable.ResumableFile(resumable, {
      name: 'image.jpg',
      type: 'image/png'
    });
  });

  it('should get type', function() {
    expect(file.getType()).toBe('png');
    file.file.type = '';
    expect(file.getType()).toBe('');
  });

  it('should get extension', function() {
    expect(file.name).toBe('image.jpg');
    expect(file.getExtension()).toBe('jpg');
    file.name = '';
    expect(file.getExtension()).toBe('');
    file.name = 'image';
    expect(file.getExtension()).toBe('');
    file.name = '.dwq.dq.wd.qdw.E';
    expect(file.getExtension()).toBe('e');
  });
});<|endoftext|>


<commit_before>/**
*APACHE 2.0 LICENSE
*@author: Razak Zakari <razzsense@gmail.com>
 */
(function( $ ){

	/**
	 * [tagComplete Tag+AutoComplete]
	 * @param  {[object]} userOpts [user options object]
	 * @return {[object]}          [plugin instances]
	 */
	$.fn.tagComplete = function(userOpts){

		var defaultOpts = {

			//hide
			//wether the input should be hidden or not
			hide: false,

			//keyLimit
			//input limit to start the ajax
			//request
			keyLimit: 1,

			//tokenizer
			tokenizer: ",",

			//free input,allow use to insert his
			//own tag data
			freeInput : true,

			//free edit
			//free edit allows the backspace to
			//edit the tag, this can provide
			//undesired results ,
			//also freeInput is required
			//for this to work
			freeEdit : true,

			//autocomplete
			autocomplete: {

				//Auto complete data
				data: [],

				//incase of remote , autocomplete
				//remote options, same as jqery Ajax
				//options
				ajaxOpts: {
					//url: "",
					method: 'GET',
					dataType: 'json',
					data: {}
				},

				//set remote query parameters
				//for ajax only
				params : function(value){
					return {q: value,lol: 23};
				},//end params

				/*
				*After Ajax request ,proccess
				*the data so it will be well
				*formatted for the plugin
				*the returned data must be in the format
				*of {key:text} where key is the unique id
				 */
				proccessData: function(data){
					return data;
				}

			},//end auto complete

			//triggers when a new tag is added
			onAdd: function(data){
				return true;
			},//end on Add

			//ondelete
			//triggers when a tag is deleted
			onDelete: function(data){
				return true;
			}//end on delete

		};//end default objects

		//set default options
		var options = $.extend(true,defaultOpts,userOpts);

		//console.log(options);
		//

		//keycode
		backspaceKey = 8;

		//enter Key
		enterKey = 13;

	    //destory plugin
		this.destroy = function(){

			//console.log(this.destroy);

			var instanceId = $(this).data("tagcomplete-id");

			//console.log(instanceId);

			if(!instanceId){
				return false;
			}

			var tagCompleteDom = document.getElementById(instanceId);

			//remove
			$(tagCompleteDom).remove();

			//show original input
			//remove the data id
			$(this).removeData();

			$(this).val("");

			//add new data
			refreshDom = new Date().getTime()+Math.random();

			//set refresh string
			$(this).attr("data-tagcomplete-refresh",refreshDom);

			$(this).addClass("tag-complete-dead").removeClass('hide');
		} //end

		//reinit plugin
		this.reInit = function(userOptions){

			//destroy plugin first
			this.destroy();

			//if user wishes to change some options
			//then lets merge option changes
			if(userOptions){
				options = $.extend(true,options,userOptions);
			}//end if

			//then recreate plugin
			return $.fn.tagComplete.call(this,options);

			//console.log(options);
		}//end

		//proccess plugin
		return this.each(function(instanceNo,userInputDom){

			//set ajax pool to contain all requests
			ajaxPool = [];

			//lets get the input
			userInput = self = $(userInputDom);

			//hide the input
			userInput.addClass('hide');

			instanceNo = instanceNo+1;

			instanceId = "";

			var instanceId = instanceNo+(new Date()).getTime()+
								Math.floor(Math.random());

			hideClass = (options.hide == true) ? "hide" : "";

			//main container
			tagCompleteMain = $("
"+
									"
"+
									"

"+
									"<input type='text' class='tag_input' />"+
									"
"+
									"

"+
									"
"
								);

			//set the instance id
			userInput.attr("data-tagcomplete-id",instanceId);

			//insert the tagCompleteMain right after the
			//user input tag
			tagCompleteMain.insertAfter(userInput);

			//tagComplete
			tagComplete = tagCompleteMain.find(".tag_complete");

			//tag Input
			tagInput = tagComplete.find(".tag_input");

			//tags container
			tagsContainer = tagComplete.find(".tags_container");

			//auto complete
			autoComplete = tagCompleteMain.find(".autocomplete");

			//domSelectors
			//register the dom and instance
			instanceData = {
				userInput : userInput,
				tagCompleteMain: tagCompleteMain,
				tagComplete: tagComplete,
				tagsContainer: tagsContainer,
				tagInput: tagInput,
				autoComplete: autoComplete,
				options: options,
				ajaxPool: ajaxPool
			};//end dom obj

			this.instance = instanceData;

			//focus input if the tags_container is clicked
			tagComplete.on('click',instanceData,function(e){
				//focus the tagInput
				e.data.tagInput.focus();
			});//end if

			//tag Input on focus , focus tagComplete
			//container
			tagInput.on("focus",instanceData,function(e){
				e.data.tagComplete.addClass("is_focused");
			})
			.on("blur",instanceData,function(e){
				e.data.tagComplete.removeClass("is_focused");
			});//end tagComplete focus styling

			//if the auto complete child ,li is clicked
			//,then we
			//add tag
			autoComplete.on('click','li',instanceData,function(e){

				//instance
				inst = e.data;

				//console.log(inst);

				//id
				var id = $(this).data('id');

				//add tag
				$.fn.addTag(id,$(this).text(),inst);

				//refresh User input
				$.fn.refreshUserInput(inst.userInput,inst.tagsContainer,inst.options);

				//empty value
				tagInput.val("");

				//stop ajax requests
				$.fn.abortAjax(ajaxPool);

				//clear autocomplete
				$.fn.clearAutoComplete(inst.autoComplete);
			});//end onclick

			//if the tag close button is clicked
			tagsContainer.on("click",".tag .close",instanceData,function(e){

				//instance data
				inst = e.data;

				tag = $(this).parent(".tag");

				//delete tag
				$.fn.deleteTag(tag,inst);
			});//end onTag Close

			//lets listen if enter key is pressed and not empty
			tagInput.on("keyup",instanceData,function(e){

				//instance data
				instance = e.data;

				//deflate the instance data
				tagCompleteMain = instance.tagCompleteMain;
				tagComplete = instance.tagComplete;
				tagsContainer = instance.tagsContainer;
				self = tagInput = instance.tagInput;
				autoComplete = instance.autoComplete;
				options = instance.options;
				ajaxPool = instance.ajaxPool;

				value = $(this).val();

				//console.log(value);

				keycode = (e.keyCode ? e.keyCode : e.which);

				//if the value is empty , set auto complete to 0
				if(value.length == 0){
					$.fn.clearAutoComplete(autoComplete);
				}//end

				//if value is empty and the backspace is pressed
				//lets delete last input and also populate
				//the input with it
				if(value.length == 0 &&
				   keycode == backspaceKey){

					//get lastTag
					lastTagNo = tagsContainer.find('.tag').length;

					//if empty abort
					if(lastTagNo == 0){
						return false;
					}//end if

					lastTagInfo = $.fn.getTagInfo(tagsContainer,lastTagNo);

					//delete tag
					$.fn.deleteTag(lastTagInfo.selector,instance);

					//if free edit and free input is true
					//use back key to edit tag data
					if((options.freeEdit &&
						options.freeInput) ==true){
					//update the tags input text to the deleted
					//also this will help move the cursor to the end
					tagInput.focus().val(lastTagInfo.text);

					}//end if

					//stop exec
					return self;
				}//end if the key is back key

				//check if enter is pressed &&
				//tag is not empty then add tag
				//if the key too is tokenizer set
				//create tag
				else if((keycode == enterKey || e.key == options.tokenizer)
						&& value.length > 0
						&& options.freeInput == true){

					//trim last tokenizer
					if(value.endsWith(options.tokenizer)){
						value = value.slice(0,value.lastIndexOf(options.tokenizer));
					}//end if

					value = $.trim(value);

					if(value.length == 0){

						$(this).val("");

						return self;
					}

					//add tag
					// key, value, instance
					$.fn.addTag(value,value,instance);

					//empty value
					$(this).val("");

					return self;
				}//end if enter is pressed

				//user provided autocomplete data
				autoCompeleteData = options.autocomplete.data;

				//matched contents
				matchedData = {};

				//if freeinput is enabled
				if(options.freeInput == true){
					//always the first data will be the value
					matchedData[value] = value;
				}//end if

				//if we are here, then mean auto complete
				//url or local source is set
				//if value limit is less than keyslimit,then
				//abort
				if(value.length < options.keyLimit){
					return self;
				}//end if value length is less than keyslimit

				//if data is available
				if(typeof options.autocomplete.data == 'object'){

					//loop the data to get keywords
					for(i=0;i<autoCompeleteData.length;i++){

						dataWord = autoCompeleteData[i];

						//if we dont have a match
						if(dataWord.indexOf(value) == -1){
							continue;
						}//end if

						//add to matched dropdown Data
						//using the word as key prevent
						//duplicate values
						matchedData[dataWord] = dataWord;

						}//end for loop

						//upldate the drop down first
						//before the network request
						$.fn.updateAutoComplete(autoComplete,matchedData);
					}//end if options.data an array or obj

					//dataUrl
					dataUrl = options.autocomplete.ajaxOpts.url;

					//now if we have url set
					if(dataUrl != null){

						ajaxOpts = options.autocomplete.ajaxOpts;

						params = options.autocomplete.params.call(null,value);

						//set ajax data
						ajaxOpts.data = $.extend(options.data,params);

						//console.log(ajaxOpts.data);

						ajaxReq = $.ajax(ajaxOpts)
							.success(function(data){

							//proccess data
							proccessedData = options.autocomplete.proccessData.call(null,data);

							//if empty we will abort
							if(proccessedData.length==0){
								return false;
							}//end if

							//add to the local data
							$.extend(matchedData,proccessedData);

							//update auto complete
							$.fn.updateAutoComplete(autoComplete,matchedData);
						});//end ajaxReq

					}//end if the url is provided

			});//end onkeyup ...

		});//end on each loop

	};//end jq module name

	/**
	 * Module Functions
	 */

	/**
	 * addTag
	 */
	$.fn.addTag = function(tagId,tagText,instance){

		closeTag = "";

		tag = $(""+
				tagText+closeTag+
				"");

		//append tag
		instance.tagsContainer.append(tag);

		//trigger onAdd
		instance.options.onAdd.call(null,{tagId:tagText});

		//stop ajax requests
		$.fn.abortAjax(instance.ajaxPool);

		//clear autocomplete
		$.fn.clearAutoComplete(instance.autoComplete);

		//refresh User input
		$.fn.refreshUserInput(instance.userInput,
							  instance.tagsContainer,
							  instance.options);
		return true;
	};//end add Tag

	//getTag
	$.fn.getTagInfo = function(tagsContainer,child){

		selector = ".tag:nth-child("+child+")";

		tagDom = tagsContainer.find(selector);

		tagText = tagDom.text();

		tagId = tagDom.data('id');

		tagInfo = {
			id: tagId,
			text: tagText,
			selector: tagDom
		}

		return tagInfo;
	}//end getTag

	//update AutoComplete
	$.fn.updateAutoComplete = function(
								autoCompleteDom,
								data){

		var listData = "";

		//lets loop the data
		$.each(data,function(id,text){

			//build list
			listData += "
"+text+"
";
		});//end loop

		//add autocomplete item
		autoCompleteDom.empty().html(listData).removeClass('hide');

		autoCompleteDom.parent('.tag_complete_main')
						.find('.tag_complete')
						.addClass('has_autocomplete');
	};//end updateAutoComplete

	//clear autoComplete
	$.fn.clearAutoComplete = function(autoCompleteDom){

		autoCompleteDom.addClass('hide').empty();

		autoCompleteDom.parent('.tag_complete_main')
						.find('.tag_complete')
						.removeClass('has_autocomplete');
	}//end clear AutoComplete

	//abort ajaxRequest
	$.fn.abortAjax = function(ajaxPool){

		if(ajaxPool.length > 0){
			//stop all ajax pool
			$.each(ajaxPool,function(id,ajaxReq){
				ajaxReq.abort();
			});
		}//end if ajax pool isnt empty

	};//end abort pool

	/**
	 * refreshUserInput
	 */
	 $.fn.refreshUserInput = function(
	 						userInput,
	 						tagsContainer,
	 						options
	 						){

	 	//get all the tags data , tokenize it
	 	tokenizer = options.tokenizer;

	 	tokenizedData = "";

	 	//get all tags
	 	tags = tagsContainer.find(".tag");

	 	//if no tag, return false
	 	if(tags.length == 0){
	 		return false;
	 	}//end

	 	//loop to get keys
	 	$.each(tags,function(key,tagObj){

	 		//lets get the data id
	 		tagData = $(this).data("id") || $(this).text();

	 		tokenizedData += tagData+",";
	 	});//end loop

	 	//remove trailing ,
	 	tokenizedData = tokenizedData.slice(0,tokenizedData.lastIndexOf(","));

	 	//console.log(tokenizedData);
	 	userInput.val(tokenizedData);
	 };//end refresh userInput

	 /**
	  * deleteTag
	  */
	 $.fn.deleteTag = function(tag,instance){

	 	//lets get the tag info
	 	var tagId = $(tag).data("id");
	 	var tagText = $(tag).text();

	 	//delete tag
	 	tag.remove();

	 	//call on Delete
	 	instance.options.onDelete.call(null,{
	 										id:tagId,
	 										text:tagText
	 									});

	 	//refresh User Input
		$.fn.refreshUserInput(instance.userInput,instance.tagsContainer,instance.options);

		return true;
	 }//end delete

	 /**
	  * End Module Functions
	  */

}( jQuery ));<commit_msg>updateing ajax for jQuery 2->3 (.done instead of .success)<commit_after>/**
*APACHE 2.0 LICENSE
*@author: Razak Zakari <razzsense@gmail.com>
 */
(function( $ ){

	/**
	 * [tagComplete Tag+AutoComplete]
	 * @param  {[object]} userOpts [user options object]
	 * @return {[object]}          [plugin instances]
	 */
	$.fn.tagComplete = function(userOpts){

		var defaultOpts = {

			//hide
			//wether the input should be hidden or not
			hide: false,

			//keyLimit
			//input limit to start the ajax
			//request
			keyLimit: 1,

			//tokenizer
			tokenizer: ",",

			//free input,allow use to insert his
			//own tag data
			freeInput : true,

			//free edit
			//free edit allows the backspace to
			//edit the tag, this can provide
			//undesired results ,
			//also freeInput is required
			//for this to work
			freeEdit : true,

			//autocomplete
			autocomplete: {

				//Auto complete data
				data: [],

				//incase of remote , autocomplete
				//remote options, same as jqery Ajax
				//options
				ajaxOpts: {
					//url: "",
					method: 'GET',
					dataType: 'json',
					data: {}
				},

				//set remote query parameters
				//for ajax only
				params : function(value){
					return {q: value,lol: 23};
				},//end params

				/*
				*After Ajax request ,proccess
				*the data so it will be well
				*formatted for the plugin
				*the returned data must be in the format
				*of {key:text} where key is the unique id
				 */
				proccessData: function(data){
					return data;
				}

			},//end auto complete

			//triggers when a new tag is added
			onAdd: function(data){
				return true;
			},//end on Add

			//ondelete
			//triggers when a tag is deleted
			onDelete: function(data){
				return true;
			}//end on delete

		};//end default objects

		//set default options
		var options = $.extend(true,defaultOpts,userOpts);

		//console.log(options);
		//

		//keycode
		backspaceKey = 8;

		//enter Key
		enterKey = 13;

	    //destory plugin
		this.destroy = function(){

			//console.log(this.destroy);

			var instanceId = $(this).data("tagcomplete-id");

			//console.log(instanceId);

			if(!instanceId){
				return false;
			}

			var tagCompleteDom = document.getElementById(instanceId);

			//remove
			$(tagCompleteDom).remove();

			//show original input
			//remove the data id
			$(this).removeData();

			$(this).val("");

			//add new data
			refreshDom = new Date().getTime()+Math.random();

			//set refresh string
			$(this).attr("data-tagcomplete-refresh",refreshDom);

			$(this).addClass("tag-complete-dead").removeClass('hide');
		} //end

		//reinit plugin
		this.reInit = function(userOptions){

			//destroy plugin first
			this.destroy();

			//if user wishes to change some options
			//then lets merge option changes
			if(userOptions){
				options = $.extend(true,options,userOptions);
			}//end if

			//then recreate plugin
			return $.fn.tagComplete.call(this,options);

			//console.log(options);
		}//end

		//proccess plugin
		return this.each(function(instanceNo,userInputDom){

			//set ajax pool to contain all requests
			ajaxPool = [];

			//lets get the input
			userInput = self = $(userInputDom);

			//hide the input
			userInput.addClass('hide');

			instanceNo = instanceNo+1;

			instanceId = "";

			var instanceId = instanceNo+(new Date()).getTime()+
								Math.floor(Math.random());

			hideClass = (options.hide == true) ? "hide" : "";

			//main container
			tagCompleteMain = $("
"+
									"
"+
									"

"+
									"<input type='text' class='tag_input' />"+
									"
"+
									"

"+
									"
"
								);

			//set the instance id
			userInput.attr("data-tagcomplete-id",instanceId);

			//insert the tagCompleteMain right after the
			//user input tag
			tagCompleteMain.insertAfter(userInput);

			//tagComplete
			tagComplete = tagCompleteMain.find(".tag_complete");

			//tag Input
			tagInput = tagComplete.find(".tag_input");

			//tags container
			tagsContainer = tagComplete.find(".tags_container");

			//auto complete
			autoComplete = tagCompleteMain.find(".autocomplete");

			//domSelectors
			//register the dom and instance
			instanceData = {
				userInput : userInput,
				tagCompleteMain: tagCompleteMain,
				tagComplete: tagComplete,
				tagsContainer: tagsContainer,
				tagInput: tagInput,
				autoComplete: autoComplete,
				options: options,
				ajaxPool: ajaxPool
			};//end dom obj

			this.instance = instanceData;

			//focus input if the tags_container is clicked
			tagComplete.on('click',instanceData,function(e){
				//focus the tagInput
				e.data.tagInput.focus();
			});//end if

			//tag Input on focus , focus tagComplete
			//container
			tagInput.on("focus",instanceData,function(e){
				e.data.tagComplete.addClass("is_focused");
			})
			.on("blur",instanceData,function(e){
				e.data.tagComplete.removeClass("is_focused");
			});//end tagComplete focus styling

			//if the auto complete child ,li is clicked
			//,then we
			//add tag
			autoComplete.on('click','li',instanceData,function(e){

				//instance
				inst = e.data;

				//console.log(inst);

				//id
				var id = $(this).data('id');

				//add tag
				$.fn.addTag(id,$(this).text(),inst);

				//refresh User input
				$.fn.refreshUserInput(inst.userInput,inst.tagsContainer,inst.options);

				//empty value
				tagInput.val("");

				//stop ajax requests
				$.fn.abortAjax(ajaxPool);

				//clear autocomplete
				$.fn.clearAutoComplete(inst.autoComplete);
			});//end onclick

			//if the tag close button is clicked
			tagsContainer.on("click",".tag .close",instanceData,function(e){

				//instance data
				inst = e.data;

				tag = $(this).parent(".tag");

				//delete tag
				$.fn.deleteTag(tag,inst);
			});//end onTag Close

			//lets listen if enter key is pressed and not empty
			tagInput.on("keyup",instanceData,function(e){

				//instance data
				instance = e.data;

				//deflate the instance data
				tagCompleteMain = instance.tagCompleteMain;
				tagComplete = instance.tagComplete;
				tagsContainer = instance.tagsContainer;
				self = tagInput = instance.tagInput;
				autoComplete = instance.autoComplete;
				options = instance.options;
				ajaxPool = instance.ajaxPool;

				value = $(this).val();

				//console.log(value);

				keycode = (e.keyCode ? e.keyCode : e.which);

				//if the value is empty , set auto complete to 0
				if(value.length == 0){
					$.fn.clearAutoComplete(autoComplete);
				}//end

				//if value is empty and the backspace is pressed
				//lets delete last input and also populate
				//the input with it
				if(value.length == 0 &&
				   keycode == backspaceKey){

					//get lastTag
					lastTagNo = tagsContainer.find('.tag').length;

					//if empty abort
					if(lastTagNo == 0){
						return false;
					}//end if

					lastTagInfo = $.fn.getTagInfo(tagsContainer,lastTagNo);

					//delete tag
					$.fn.deleteTag(lastTagInfo.selector,instance);

					//if free edit and free input is true
					//use back key to edit tag data
					if((options.freeEdit &&
						options.freeInput) ==true){
					//update the tags input text to the deleted
					//also this will help move the cursor to the end
					tagInput.focus().val(lastTagInfo.text);

					}//end if

					//stop exec
					return self;
				}//end if the key is back key

				//check if enter is pressed &&
				//tag is not empty then add tag
				//if the key too is tokenizer set
				//create tag
				else if((keycode == enterKey || e.key == options.tokenizer)
						&& value.length > 0
						&& options.freeInput == true){

					//trim last tokenizer
					if(value.endsWith(options.tokenizer)){
						value = value.slice(0,value.lastIndexOf(options.tokenizer));
					}//end if

					value = $.trim(value);

					if(value.length == 0){

						$(this).val("");

						return self;
					}

					//add tag
					// key, value, instance
					$.fn.addTag(value,value,instance);

					//empty value
					$(this).val("");

					return self;
				}//end if enter is pressed

				//user provided autocomplete data
				autoCompeleteData = options.autocomplete.data;

				//matched contents
				matchedData = {};

				//if freeinput is enabled
				if(options.freeInput == true){
					//always the first data will be the value
					matchedData[value] = value;
				}//end if

				//if we are here, then mean auto complete
				//url or local source is set
				//if value limit is less than keyslimit,then
				//abort
				if(value.length < options.keyLimit){
					return self;
				}//end if value length is less than keyslimit

				//if data is available
				if(typeof options.autocomplete.data == 'object'){

					//loop the data to get keywords
					for(i=0;i<autoCompeleteData.length;i++){

						dataWord = autoCompeleteData[i];

						//if we dont have a match
						if(dataWord.indexOf(value) == -1){
							continue;
						}//end if

						//add to matched dropdown Data
						//using the word as key prevent
						//duplicate values
						matchedData[dataWord] = dataWord;

						}//end for loop

						//upldate the drop down first
						//before the network request
						$.fn.updateAutoComplete(autoComplete,matchedData);
					}//end if options.data an array or obj

					//dataUrl
					dataUrl = options.autocomplete.ajaxOpts.url;

					//now if we have url set
					if(dataUrl != null){

						ajaxOpts = options.autocomplete.ajaxOpts;

						params = options.autocomplete.params.call(null,value);

						//set ajax data
						ajaxOpts.data = $.extend(options.data,params);

						//console.log(ajaxOpts.data);

						ajaxReq = $.ajax(ajaxOpts)
							.done(function(data){

							//proccess data
							proccessedData = options.autocomplete.proccessData.call(null,data);

							//if empty we will abort
							if(proccessedData.length==0){
								return false;
							}//end if

							//add to the local data
							$.extend(matchedData,proccessedData);

							//update auto complete
							$.fn.updateAutoComplete(autoComplete,matchedData);
						});//end ajaxReq

					}//end if the url is provided

			});//end onkeyup ...

		});//end on each loop

	};//end jq module name

	/**
	 * Module Functions
	 */

	/**
	 * addTag
	 */
	$.fn.addTag = function(tagId,tagText,instance){

		closeTag = "";

		tag = $(""+
				tagText+closeTag+
				"");

		//append tag
		instance.tagsContainer.append(tag);

		//trigger onAdd
		instance.options.onAdd.call(null,{tagId:tagText});

		//stop ajax requests
		$.fn.abortAjax(instance.ajaxPool);

		//clear autocomplete
		$.fn.clearAutoComplete(instance.autoComplete);

		//refresh User input
		$.fn.refreshUserInput(instance.userInput,
							  instance.tagsContainer,
							  instance.options);
		return true;
	};//end add Tag

	//getTag
	$.fn.getTagInfo = function(tagsContainer,child){

		selector = ".tag:nth-child("+child+")";

		tagDom = tagsContainer.find(selector);

		tagText = tagDom.text();

		tagId = tagDom.data('id');

		tagInfo = {
			id: tagId,
			text: tagText,
			selector: tagDom
		}

		return tagInfo;
	}//end getTag

	//update AutoComplete
	$.fn.updateAutoComplete = function(
								autoCompleteDom,
								data){

		var listData = "";

		//lets loop the data
		$.each(data,function(id,text){

			//build list
			listData += "
"+text+"
";
		});//end loop

		//add autocomplete item
		autoCompleteDom.empty().html(listData).removeClass('hide');

		autoCompleteDom.parent('.tag_complete_main')
						.find('.tag_complete')
						.addClass('has_autocomplete');
	};//end updateAutoComplete

	//clear autoComplete
	$.fn.clearAutoComplete = function(autoCompleteDom){

		autoCompleteDom.addClass('hide').empty();

		autoCompleteDom.parent('.tag_complete_main')
						.find('.tag_complete')
						.removeClass('has_autocomplete');
	}//end clear AutoComplete

	//abort ajaxRequest
	$.fn.abortAjax = function(ajaxPool){

		if(ajaxPool.length > 0){
			//stop all ajax pool
			$.each(ajaxPool,function(id,ajaxReq){
				ajaxReq.abort();
			});
		}//end if ajax pool isnt empty

	};//end abort pool

	/**
	 * refreshUserInput
	 */
	 $.fn.refreshUserInput = function(
	 						userInput,
	 						tagsContainer,
	 						options
	 						){

	 	//get all the tags data , tokenize it
	 	tokenizer = options.tokenizer;

	 	tokenizedData = "";

	 	//get all tags
	 	tags = tagsContainer.find(".tag");

	 	//if no tag, return false
	 	if(tags.length == 0){
	 		return false;
	 	}//end

	 	//loop to get keys
	 	$.each(tags,function(key,tagObj){

	 		//lets get the data id
	 		tagData = $(this).data("id") || $(this).text();

	 		tokenizedData += tagData+",";
	 	});//end loop

	 	//remove trailing ,
	 	tokenizedData = tokenizedData.slice(0,tokenizedData.lastIndexOf(","));

	 	//console.log(tokenizedData);
	 	userInput.val(tokenizedData);
	 };//end refresh userInput

	 /**
	  * deleteTag
	  */
	 $.fn.deleteTag = function(tag,instance){

	 	//lets get the tag info
	 	var tagId = $(tag).data("id");
	 	var tagText = $(tag).text();

	 	//delete tag
	 	tag.remove();

	 	//call on Delete
	 	instance.options.onDelete.call(null,{
	 										id:tagId,
	 										text:tagText
	 									});

	 	//refresh User Input
		$.fn.refreshUserInput(instance.userInput,instance.tagsContainer,instance.options);

		return true;
	 }//end delete

	 /**
	  * End Module Functions
	  */

}( jQuery ));
<|endoftext|>