<commit_before>var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var gcsServer = require(__dirname + '/../lib/server');
var http = require('http');
var Deferred = require('jsdeferred').Deferred;
var nativeNroonga = require('nroonga');
var wrappedNroonga = require(__dirname + '/../lib/wrapped-nroonga');
var xml2js = require('xml2js');

var temporaryDirectory = exports.temporaryDirectory = path.join(__dirname, 'tmp');

var testHost = 'localhost';
var testPort = 3333;
exports.testHost = testHost;
exports.testPort = testPort;

function setupServer(context) {
  var server = gcsServer.createServer({context: context});
  server.listen(testPort);
  return server;
}
exports.setupServer = setupServer;

function sendRequest(method, path, postData, headers) {
  var deferred = new Deferred();

  var options = {
        host: testHost,
        port: testPort,
        path: path,
        method: method,
        headers: {}
      };

  if (headers) {
    for (var header in headers) {
      if (headers.hasOwnProperty(header))
        options.headers[header] = headers[header];
    }
  }

  Deferred.next(function() {
    var request = http.request(options, function(response) {
          var body = '';
          response.on('data', function(data) {
            body += data;
          });
          response.on('end', function() {
            deferred.call({
              statusCode: response.statusCode,
              body: body
            });
          });
        });
    request.on('error', function(error) {
      deferred.fail(error);
    });

    if (postData) request.write(postData);
    request.end();
  });

  return deferred;
}

function get(path, headers) {
  return sendRequest('GET', path, null, headers);
}
exports.get = get;
Deferred.register('get', function() { return get.apply(this, arguments); });

function post(path, body, headers) {
  return sendRequest('POST', path, body, headers);
}
exports.post = post;
Deferred.register('post', function() { return post.apply(this, arguments); });

var databaseCount = 0;

exports.createTemporaryDatabase = function() {
  var databaseName = 'database-' + ++databaseCount;
  var databaseDirectory = path.join(temporaryDirectory, databaseName);
  var databasePath = path.join(databaseDirectory, 'database');
  rmRSync(databaseDirectory);
  mkdirp.sync(databaseDirectory);
  return {
    path: databasePath,
    get: function() {
      return this._context ||
             (this._context = new nativeNroonga.Database(databasePath));
    },
    clear: function() {
      var context = this._context;
      var tables = context.commandSync('table_list');
      wrappedNroonga.formatResults(tables).forEach(function(table) {
        context.commandSync('table_remove', { name: table.name });
      });
    },
    teardown: function() {
      this._context.close();
      rmRSync(databaseDirectory);
      this._context = undefined;
    }
  };
};

exports.loadDumpFile = function(context, path) {
  var dump = fs.readFileSync(path, 'UTF-8');
  dump.split('\n').forEach(function(line) {
    context.commandSyncString(line);
  });
}

function isDirectory(path) {
  return fs.statSync(path).isDirectory();
}
exports.isDirectory = isDirectory;

function rmRSync(directoryPath) {
  if (!path.existsSync(directoryPath)) return;

  var files = fs.readdirSync(directoryPath);
  var file, filePath;
  for (var i = 0, maxi = files.length; i < maxi; i++) {
    file = files[i];
    filePath = path.join(directoryPath, file);
    if (isDirectory(filePath))
      rmRSync(filePath);
    else
      fs.unlinkSync(filePath);
  }
  fs.rmdirSync(directoryPath);
}
exports.rmRSync = rmRSync;

function XMLStringToJSON(xml) {
  var xml2js = require('xml2js');
  var parser = new xml2js.Parser({
                 explicitRoot: true
               });
  var json;
  parser.addListener('end', function(result) {
    json = result;
  });
  parser.parseString(xml);
  return json;
}
exports.XMLStringToJSON = XMLStringToJSON;

// activate diff for chai.assert.deepEqual

var chai_utils = require('chai/lib/chai/utils');

function sortAndStringify(object) {
  switch (typeof object) {
    case 'string':
    case 'number':
    case 'boolean':
      return JSON.stringify(object);
    default:
      if (Array.isArray(object)) {
        return '[' + object.map(function(item) { return sortAndStringify(item); }).join(', ') + ']';
      } else if (!object) {
        return JSON.stringify(object);
      } else {
        var sorted = {};
        Object.keys(object).sort().forEach(function(key) {
          if (object.hasOwnProperty(key))
            sorted[key] = object[key];
        });
        return JSON.stringify(sorted);
      }
  }
}

require('chai').Assertion.prototype.eql = function(obj) {
  var expected = chai_utils.flag(this, 'object');
  this.assert(
      chai_utils.eql(obj, expected)
    , 'expected #{this} to deeply equal #{exp}'
    , 'expected #{this} to not deeply equal #{exp}'
    , sortAndStringify(expected)
    , sortAndStringify(obj)
  );
};
<commit_msg>Output error XML for parsing error<commit_after>var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var gcsServer = require(__dirname + '/../lib/server');
var http = require('http');
var Deferred = require('jsdeferred').Deferred;
var nativeNroonga = require('nroonga');
var wrappedNroonga = require(__dirname + '/../lib/wrapped-nroonga');
var xml2js = require('xml2js');

var temporaryDirectory = exports.temporaryDirectory = path.join(__dirname, 'tmp');

var testHost = 'localhost';
var testPort = 3333;
exports.testHost = testHost;
exports.testPort = testPort;

function setupServer(context) {
  var server = gcsServer.createServer({context: context});
  server.listen(testPort);
  return server;
}
exports.setupServer = setupServer;

function sendRequest(method, path, postData, headers) {
  var deferred = new Deferred();

  var options = {
        host: testHost,
        port: testPort,
        path: path,
        method: method,
        headers: {}
      };

  if (headers) {
    for (var header in headers) {
      if (headers.hasOwnProperty(header))
        options.headers[header] = headers[header];
    }
  }

  Deferred.next(function() {
    var request = http.request(options, function(response) {
          var body = '';
          response.on('data', function(data) {
            body += data;
          });
          response.on('end', function() {
            deferred.call({
              statusCode: response.statusCode,
              body: body
            });
          });
        });
    request.on('error', function(error) {
      deferred.fail(error);
    });

    if (postData) request.write(postData);
    request.end();
  });

  return deferred;
}

function get(path, headers) {
  return sendRequest('GET', path, null, headers);
}
exports.get = get;
Deferred.register('get', function() { return get.apply(this, arguments); });

function post(path, body, headers) {
  return sendRequest('POST', path, body, headers);
}
exports.post = post;
Deferred.register('post', function() { return post.apply(this, arguments); });

var databaseCount = 0;

exports.createTemporaryDatabase = function() {
  var databaseName = 'database-' + ++databaseCount;
  var databaseDirectory = path.join(temporaryDirectory, databaseName);
  var databasePath = path.join(databaseDirectory, 'database');
  rmRSync(databaseDirectory);
  mkdirp.sync(databaseDirectory);
  return {
    path: databasePath,
    get: function() {
      return this._context ||
             (this._context = new nativeNroonga.Database(databasePath));
    },
    clear: function() {
      var context = this._context;
      var tables = context.commandSync('table_list');
      wrappedNroonga.formatResults(tables).forEach(function(table) {
        context.commandSync('table_remove', { name: table.name });
      });
    },
    teardown: function() {
      this._context.close();
      rmRSync(databaseDirectory);
      this._context = undefined;
    }
  };
};

exports.loadDumpFile = function(context, path) {
  var dump = fs.readFileSync(path, 'UTF-8');
  dump.split('\n').forEach(function(line) {
    context.commandSyncString(line);
  });
}

function isDirectory(path) {
  return fs.statSync(path).isDirectory();
}
exports.isDirectory = isDirectory;

function rmRSync(directoryPath) {
  if (!path.existsSync(directoryPath)) return;

  var files = fs.readdirSync(directoryPath);
  var file, filePath;
  for (var i = 0, maxi = files.length; i < maxi; i++) {
    file = files[i];
    filePath = path.join(directoryPath, file);
    if (isDirectory(filePath))
      rmRSync(filePath);
    else
      fs.unlinkSync(filePath);
  }
  fs.rmdirSync(directoryPath);
}
exports.rmRSync = rmRSync;

function XMLStringToJSON(xml) {
  var xml2js = require('xml2js');
  var parser = new xml2js.Parser({
                 explicitRoot: true
               });
  var json;
  parser.addListener('end', function(result) {
    json = result;
  });
  try {
    parser.parseString(xml);
  } catch(e) {
    console.log(xml);
    throw e;
  }
  return json;
}
exports.XMLStringToJSON = XMLStringToJSON;

// activate diff for chai.assert.deepEqual

var chai_utils = require('chai/lib/chai/utils');

function sortAndStringify(object) {
  switch (typeof object) {
    case 'string':
    case 'number':
    case 'boolean':
      return JSON.stringify(object);
    default:
      if (Array.isArray(object)) {
        return '[' + object.map(function(item) { return sortAndStringify(item); }).join(', ') + ']';
      } else if (!object) {
        return JSON.stringify(object);
      } else {
        var sorted = {};
        Object.keys(object).sort().forEach(function(key) {
          if (object.hasOwnProperty(key))
            sorted[key] = object[key];
        });
        return JSON.stringify(sorted);
      }
  }
}

require('chai').Assertion.prototype.eql = function(obj) {
  var expected = chai_utils.flag(this, 'object');
  this.assert(
      chai_utils.eql(obj, expected)
    , 'expected #{this} to deeply equal #{exp}'
    , 'expected #{this} to not deeply equal #{exp}'
    , sortAndStringify(expected)
    , sortAndStringify(obj)
  );
};
<|endoftext|>


<commit_before><commit_msg>Fixed issue with ` ` in the chat-textfield.<commit_after><|endoftext|>


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

function createMockedSender() {
  var sender = {
    emit: function(eventName, message) {
      this.messages.push({ eventName: eventName, message: message });
    },
    assertSent: function(eventName, message) {
      var firstMessage = this.messages.shift();
      var expectedMessage = { eventName: eventName, message: message };
      assert.deepEqual(firstMessage, expectedMessage);
    },
    messages: [],
  };
  return sender;
}
exports.createMockedSender = createMockedSender;

function createMockedReceiver() {
  var mockedSockets;
  var mockedReceiverInternal = nodemock;
  var connactionCallbackController = {};
  var messageCallbackController = {};
  var receiver = {
    // mocking receiver
    sockets:
      (mockedSockets = nodemock.mock('on')
         .takes('connection', function() {})
         .ctrl(1, connactionCallbackController)),
    'set': function(key, value) {},

    // extra features as a mocked object
    triggerConnect: function(tag) {
      mockedSockets.assertThrows();
      var mockedSocket = nodemock.mock('on')
                           .takes(tag + '.message', function() {})
                           .ctrl(1, messageCallbackController);
      connactionCallbackController.trigger(mockedSocket);
      mockedSocket.assertThrows();
    },
    emitMessage: function(message) { // simulate message from backend
      messageCallbackController.trigger(message);
    }
  };
  return receiver;
}
exports.createMockedReceiver = createMockedReceiver;

function createMockedMessageCallback() {
  var mockedCallback = nodemock;
  var callback = function(message) {
    mockedCallback.receive(message);
  };
  callback.takes = function(message) {
    callback.assert = function() {
      mockedCallback.assertThrows();
    };
    mockedCallback = mockedCallback
                       .mock('receive')
                       .takes(message);
  };
  callback.mock = mockedCallback;
  return callback;
}
exports.createMockedMessageCallback = createMockedMessageCallback;

function TypeOf(typeString) {
  if (!(this instanceof TypeOf))
    return new TypeOf(typeString);

  this.typeString = typeString;
  if (typeString == 'date') {
    return new InstanceOf(Date);
  }
}
exports.TypeOf = TypeOf;

function InstanceOf(constructor) {
  if (!(this instanceof InstanceOf))
    return new InstanceOf(constructor);

  this.constructorFunction = constructor;
}
exports.InstanceOf = InstanceOf;

function assertEnvelopeEqual(actual, expected) {
  var vs = JSON.stringify(actual) + ' vs ' + JSON.stringify(expected);
  Object.keys(expected).forEach(function(key) {
    var actualValue = actual[key];
    var expectedValue = expected[key];
    if (expectedValue instanceof InstanceOf) {
      if (typeof actualValue == 'string') {
        // Try fo parse the value and create new instance.
        // If this process is failed, it can be an invalid value.
        actualValue = new expectedValue.constructorFunction(actualValue);
      }
      assert.instanceOf(actualValue,
                        expectedValue.constructorFunction,
                        key + ' / ' + vs);
    } else if (expectedValue instanceof TypeOf) {
      assert.typeOf(typeof actualValue,
                    expectedValue.typeString,
                    key + ' / ' + vs);
    } else {
      assert.deepEqual(actualValue, expectedValue, key + ' / ' + vs);
    }
  });
}
assert.envelopeEqual = assertEnvelopeEqual;

function sortKeys(original) {
  if (!original || typeof original != 'object')
    return original;

  if (Array.isArray(original))
    return original.map(sortKeys);

  var sorted = {};
  Object.keys(original).sort().forEach(function(key) {
    sorted[key] = sortKeys(original[key]);
  });
  return sorted;
}

// assert.deepEqual fails when the order of hash keys are different,
// even if they are "eaual" as JSON objects.
function assertEqualJSON(actual, expected) {
  this.deepEqual(sortKeys(actual), sortKeys(expected));
}
assert.equalJSON = assertEqualJSON;
<commit_msg>Add test utilities for HTTP REST APIs<commit_after>var assert = require('chai').assert;
var nodemock = require('nodemock');
var Deferred = require('jsdeferred').Deferred;

function createMockedSender() {
  var sender = {
    emit: function(eventName, message) {
      this.messages.push({ eventName: eventName, message: message });
    },
    assertSent: function(eventName, message) {
      var firstMessage = this.messages.shift();
      var expectedMessage = { eventName: eventName, message: message };
      assert.deepEqual(firstMessage, expectedMessage);
    },
    messages: [],
  };
  return sender;
}
exports.createMockedSender = createMockedSender;

function createMockedReceiver() {
  var mockedSockets;
  var mockedReceiverInternal = nodemock;
  var connactionCallbackController = {};
  var messageCallbackController = {};
  var receiver = {
    // mocking receiver
    sockets:
      (mockedSockets = nodemock.mock('on')
         .takes('connection', function() {})
         .ctrl(1, connactionCallbackController)),
    'set': function(key, value) {},

    // extra features as a mocked object
    triggerConnect: function(tag) {
      mockedSockets.assertThrows();
      var mockedSocket = nodemock.mock('on')
                           .takes(tag + '.message', function() {})
                           .ctrl(1, messageCallbackController);
      connactionCallbackController.trigger(mockedSocket);
      mockedSocket.assertThrows();
    },
    emitMessage: function(message) { // simulate message from backend
      messageCallbackController.trigger(message);
    }
  };
  return receiver;
}
exports.createMockedReceiver = createMockedReceiver;

function createMockedMessageCallback() {
  var mockedCallback = nodemock;
  var callback = function(message) {
    mockedCallback.receive(message);
  };
  callback.takes = function(message) {
    callback.assert = function() {
      mockedCallback.assertThrows();
    };
    mockedCallback = mockedCallback
                       .mock('receive')
                       .takes(message);
  };
  callback.mock = mockedCallback;
  return callback;
}
exports.createMockedMessageCallback = createMockedMessageCallback;

var testPort = 3333;

function setupServer(handler) {
  var server = http.createServer(handler);
  server.listen(testPort);
  return server;
}
exports.setupServer = setupServer;

function sendRequest(method, path, postData, headers) {
  var deferred = new Deferred();

  var options = {
        host: 'localhost',
        port: testPort,
        path: path,
        method: method,
        headers: {}
      };

  if (headers) {
    for (var header in headers) {
      if (headers.hasOwnProperty(header))
        options.headers[header] = headers[header];
    }
  }

  Deferred.next(function() {
    var request = http.request(options, function(response) {
          var body = '';
          response.on('data', function(data) {
            body += data;
          });
          response.on('end', function() {
            deferred.call({
              statusCode: response.statusCode,
              body: body
            });
          });
        });
    request.on('error', function(error) {
      deferred.fail(error);
    });

    if (postData) request.write(postData);
    request.end();
  });

  return deferred;
}

function get(path, headers) {
  return sendRequest('GET', path, null, headers);
}
exports.get = get;
Deferred.register('get', function() { return get.apply(this, arguments); });

function post(path, body, headers) {
  return sendRequest('POST', path, body, headers);
}
exports.post = post;
Deferred.register('post', function() { return post.apply(this, arguments); });

function TypeOf(typeString) {
  if (!(this instanceof TypeOf))
    return new TypeOf(typeString);

  this.typeString = typeString;
  if (typeString == 'date') {
    return new InstanceOf(Date);
  }
}
exports.TypeOf = TypeOf;

function InstanceOf(constructor) {
  if (!(this instanceof InstanceOf))
    return new InstanceOf(constructor);

  this.constructorFunction = constructor;
}
exports.InstanceOf = InstanceOf;

function assertEnvelopeEqual(actual, expected) {
  var vs = JSON.stringify(actual) + ' vs ' + JSON.stringify(expected);
  Object.keys(expected).forEach(function(key) {
    var actualValue = actual[key];
    var expectedValue = expected[key];
    if (expectedValue instanceof InstanceOf) {
      if (typeof actualValue == 'string') {
        // Try fo parse the value and create new instance.
        // If this process is failed, it can be an invalid value.
        actualValue = new expectedValue.constructorFunction(actualValue);
      }
      assert.instanceOf(actualValue,
                        expectedValue.constructorFunction,
                        key + ' / ' + vs);
    } else if (expectedValue instanceof TypeOf) {
      assert.typeOf(typeof actualValue,
                    expectedValue.typeString,
                    key + ' / ' + vs);
    } else {
      assert.deepEqual(actualValue, expectedValue, key + ' / ' + vs);
    }
  });
}
assert.envelopeEqual = assertEnvelopeEqual;

function sortKeys(original) {
  if (!original || typeof original != 'object')
    return original;

  if (Array.isArray(original))
    return original.map(sortKeys);

  var sorted = {};
  Object.keys(original).sort().forEach(function(key) {
    sorted[key] = sortKeys(original[key]);
  });
  return sorted;
}

// assert.deepEqual fails when the order of hash keys are different,
// even if they are "eaual" as JSON objects.
function assertEqualJSON(actual, expected) {
  this.deepEqual(sortKeys(actual), sortKeys(expected));
}
assert.equalJSON = assertEqualJSON;
<|endoftext|>


<commit_before>var sql = window.SQL;

// Create a database
var db = new sql.Database();

// Return an HTML table as a string, given SQL.js results
var table_from_results = function(res) {
  var table_string = '
';
  if (res) {
    table_string += '
';
    for (index in res[0].columns) {
      table_string += '' + res[0].columns[index] + '	';
    }
    table_string += '
';
    for (row_index in res[0].values) {
      table_string += '
';
      for (index in res[0].values[row_index]) {
        table_string += '' + res[0].values[row_index][index] + '	';
      }
      table_string += '
';
    }
  }
  table_string += '
';
  return table_string;
};

var grade_results = function(results, correct_answer) {
  if (!res) {
    return false;
  }

  return JSON.stringify(results[0].columns) == JSON.stringify(correct_answer.columns) &&
    JSON.stringify(results[0].values) == JSON.stringify(correct_answer.values);
}

var show_is_correct = function(is_correct) {
  if (is_correct) {
    is_correct_html = 'Congrats!  That is correct!
';
    if (current_level < levels.length) {
      is_correct_html += 'Next Lesson';
    } else {
      is_correct_html += 'That is currently the end of the tutorial.  Please check back later for more!';
    }
    $('#answer-correctness').html(is_correct_html);
  } else {
    $('#answer-correctness').text('That was incorrect.  Please try again.');
  }
  $('#answer-correctness').show();
};

// Onclick handler for when you click "Run SQL"
$('#sql-link').click(function() {
  var answer = levels[current_level - 1]['answer'];
  try {
    var results = db.exec($('#sql-input').val());
    $('#results').html(table_from_results(results));
    show_is_correct(grade_results(results, answer));
  } catch (err) {
    $('#results').html('');
    show_is_correct(false);
  }
  $('.expected-results-container').show();
  $('#expected-results').html(table_from_results([answer]));
  return false;
});

// Stores the prompts and answers for each level number
var levels = [{'name': 'SELECT *',
              'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28],
                                     [2, 'Mary', 'female', 'human', 27],
                                     [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In SQL, data is usually organized in various tables. For example, a sports team database might have the tables teams, players, and games. A wedding database might have tables guests, vendors, and music_playlist.

Imagine we have a table that stores family members with each member\'s name, age, species, and gender.

Let\'s start by grabbing all of the data in one table.  We have a table called "family_members" that is shown below.  In order to grab all of that data, please run the following command:
SELECT * FROM family_members;'},
              { 'name': 'WHERE ... Equals',
                'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In order to select particular rows from this table, we use the "WHERE" keyword.  So for example, if we wanted to grab all of the rows that correspond to humans, we would type "SELECT * FROM family_members WHERE species = \'human\';"  Note that the quotes have to be around the word human.  Can you run a query that returns all of the rows that refer to dogs?'},
              { 'name': 'WHERE ... Greater Than',
                'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28]]},
               'prompt': 'If we want to only select family members based on a numerical field, we can also use the "WHERE" keyword.  In SQL, > means "greater than", and < means "less than".  For example, if we wanted to select family members younger than 10, we would type "SELECT * FROM family_members WHERE age < 10;".
  Can you run a query that returns all rows of members with age greater than 27?'},
              { 'name': 'WHERE ... greater than or equal to',
                'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[2, 'Mary', 'female', 'human', 27],
                                     [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'SQL also accepts >= to mean "greater than or equal to" and <= to mean "less than or equal to". Can you run a query that returns all rows of members with an age less than or equal to 27?'}];

// Create the SQL table
var sqlstr = "CREATE TABLE family_members (id int, name char, gender char, species char, age int);";
sqlstr += "INSERT INTO family_members VALUES (1, 'Dave', 'male', 'human', 28);"
sqlstr += "INSERT INTO family_members VALUES (2, 'Mary', 'female', 'human', 27);"
sqlstr += "INSERT INTO family_members VALUES (3, 'Pickles', 'male', 'dog', 4);"
db.run(sqlstr);

var res = db.exec("SELECT * FROM family_members;");
$('#current-tables').html(table_from_results(res));

var current_level;

var load_level = function() {
  current_level = parseInt(window.location.hash.substr(2), 10) || 1;
  // Set text for current level
  lesson_name = levels[current_level-1]['name']
  $('#lesson-name').text("Lesson " + current_level + ": " + lesson_name);
  $('#prompt').html(levels[current_level-1]['prompt']);

  // Add "next" and "previous" links if it makes sense.
  if (current_level > 1) {
    $('#previous-link').attr('href', '#!' + (current_level - 1));
    $('#previous-link').show();
  } else {
    $('#previous-link').hide();
  }
  if (current_level < levels.length) {
    $('#next-link').attr('href', '#!' + (current_level + 1));
    $('#next-link').show();
  } else {
    $('#next-link').hide();
  }

  // Clear out old data
  $('#answer-correctness').hide();
  $('#sql-input').val('');
  $('#results').html('');
  $('.expected-results-container').hide();
};
load_level();

$(window).bind('hashchange', function() {
  load_level();
  ga('send', 'pageview', {'page': location.pathname + location.search  + location.hash});
});
<commit_msg>minor clean up on prompt text<commit_after>var sql = window.SQL;

// Create a database
var db = new sql.Database();

// Return an HTML table as a string, given SQL.js results
var table_from_results = function(res) {
  var table_string = '
';
  if (res) {
    table_string += '
';
    for (index in res[0].columns) {
      table_string += '' + res[0].columns[index] + '	';
    }
    table_string += '
';
    for (row_index in res[0].values) {
      table_string += '
';
      for (index in res[0].values[row_index]) {
        table_string += '' + res[0].values[row_index][index] + '	';
      }
      table_string += '
';
    }
  }
  table_string += '
';
  return table_string;
};

var grade_results = function(results, correct_answer) {
  if (!res) {
    return false;
  }

  return JSON.stringify(results[0].columns) == JSON.stringify(correct_answer.columns) &&
    JSON.stringify(results[0].values) == JSON.stringify(correct_answer.values);
}

var show_is_correct = function(is_correct) {
  if (is_correct) {
    is_correct_html = 'Congrats!  That is correct!
';
    if (current_level < levels.length) {
      is_correct_html += 'Next Lesson';
    } else {
      is_correct_html += 'That is currently the end of the tutorial.  Please check back later for more!';
    }
    $('#answer-correctness').html(is_correct_html);
  } else {
    $('#answer-correctness').text('That was incorrect.  Please try again.');
  }
  $('#answer-correctness').show();
};

// Onclick handler for when you click "Run SQL"
$('#sql-link').click(function() {
  var answer = levels[current_level - 1]['answer'];
  try {
    var results = db.exec($('#sql-input').val());
    $('#results').html(table_from_results(results));
    show_is_correct(grade_results(results, answer));
  } catch (err) {
    $('#results').html('');
    show_is_correct(false);
  }
  $('.expected-results-container').show();
  $('#expected-results').html(table_from_results([answer]));
  return false;
});

// Stores the prompts and answers for each level number
var levels = [{'name': 'SELECT *',
              'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28],
                                     [2, 'Mary', 'female', 'human', 27],
                                     [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In SQL, data is usually organized in various tables. For example, a sports team database might have the tables teams, players, and games. A wedding database might have tables guests, vendors, and music_playlist.

Imagine we have a table that stores family members with each member\'s name, age, species, and gender.

Let\'s start by grabbing all of the data in one table.  We have a table called "family_members" that is shown below.  In order to grab all of that data, please run the following command:
SELECT * FROM family_members;'},
              { 'name': 'WHERE ... Equals',
                'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'In order to select particular rows from this table, we use the "WHERE" keyword.  So for example, if we wanted to grab all of the rows that correspond to humans, we would type
SELECT * FROM family_members WHERE species = \'human\';
  Note that the quotes have to be around the word human.

Can you run a query that returns all of the rows that refer to dogs?'},
              { 'name': 'WHERE ... Greater Than',
                'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[1, 'Dave', 'male', 'human', 28]]},
               'prompt': 'If we want to only select family members based on a numerical field, we can also use the "WHERE" keyword.  For example, if we wanted to select family members older than 10, we would type
SELECT * FROM family_members WHERE age > 10;

  Can you run return all rows of members with age greater than 27?'},
              { 'name': 'WHERE ... greater than or equal to',
                'answer': {'columns': ['id', 'name', 'gender', 'species', 'age'],
                          'values': [[2, 'Mary', 'female', 'human', 27],
                                     [3, 'Pickles', 'male', 'dog', 4]]},
               'prompt': 'SQL accepts various inequality symbols, including
 >= "greater than or equal to" and
 <= "less than or equal to".

 Can you return all rows of members with an age less than or equal to 27?'}];

// Create the SQL table
var sqlstr = "CREATE TABLE family_members (id int, name char, gender char, species char, age int);";
sqlstr += "INSERT INTO family_members VALUES (1, 'Dave', 'male', 'human', 28);"
sqlstr += "INSERT INTO family_members VALUES (2, 'Mary', 'female', 'human', 27);"
sqlstr += "INSERT INTO family_members VALUES (3, 'Pickles', 'male', 'dog', 4);"
db.run(sqlstr);

var res = db.exec("SELECT * FROM family_members;");
$('#current-tables').html(table_from_results(res));

var current_level;

var load_level = function() {
  current_level = parseInt(window.location.hash.substr(2), 10) || 1;
  // Set text for current level
  lesson_name = levels[current_level-1]['name']
  $('#lesson-name').text("Lesson " + current_level + ": " + lesson_name);
  $('#prompt').html(levels[current_level-1]['prompt']);

  // Add "next" and "previous" links if it makes sense.
  if (current_level > 1) {
    $('#previous-link').attr('href', '#!' + (current_level - 1));
    $('#previous-link').show();
  } else {
    $('#previous-link').hide();
  }
  if (current_level < levels.length) {
    $('#next-link').attr('href', '#!' + (current_level + 1));
    $('#next-link').show();
  } else {
    $('#next-link').hide();
  }

  // Clear out old data
  $('#answer-correctness').hide();
  $('#sql-input').val('');
  $('#results').html('');
  $('.expected-results-container').hide();
};
load_level();

$(window).bind('hashchange', function() {
  load_level();
  ga('send', 'pageview', {'page': location.pathname + location.search  + location.hash});
});
<|endoftext|>


<commit_before>var specHelper = require('../../spec-helper');
var _ = require('underscore');
var CategoryAutoStyler = require('../../../src/widgets/auto-style/category.js');
var Backbone = require('backbone');

describe('src/widgets/auto-style/category', function () {
  beforeEach(function () {
    var vis = specHelper.createDefaultVis();
    var layer = vis.map.layers.first();
    this.dataview = new Backbone.Model({
      data: [
        { name: "soccer" },
        { name: 'basketball' },
        { name: 'baseball' },
        { name: 'handball' },
        { name: 'hockey' }
      ],
      column: 'something'
    });

    this.layer = this.dataview.layer = layer;
    this.categoryAutoStyler = new CategoryAutoStyler(this.dataview);

    // Trick to set colors properly in this test
    this.updateColorsByData = function () {
      this.categoryAutoStyler.colors.updateData(_.pluck(this.dataview.get('data'), 'name'));
    }.bind(this);

    this.updateColorsByData();
  });

  describe('.getStyle', function () {
    it('should generate the right styles when layer has polygons', function () {
      this.dataview.layer.set('initialStyle', '#layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;  polygon-fill: #e49115;  polygon-fill-opacity: 0.9; }');
      expect(this.categoryAutoStyler.getStyle()).toBe(' #layer [\'mapnik::geometry_type\'=3] { polygon-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  }  #layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;   polygon-fill-opacity: 0.9; }');
    });

    it('should generate the right styles when layer has points', function () {
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });

    it('should generate the right styles when layer has lines', function () {
      this.dataview.layer.set('initialStyle', '#layer {  line-width: 0.5;  line-color: #fcfafa;  line-opacity: 1; }');
      expect(this.categoryAutoStyler.getStyle()).toBe('#layer [\'mapnik::geometry_type\'=2] { line-color: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  }   #layer {  line-width: 0.5;   line-opacity: 1; }');
    });

    it('should generate unique attr style when layer has multiple attrs', function () {
      this.dataview.layer.set('initialStyle', '#layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;  polygon-fill: #e49115;  polygon-fill-opacity: 0.9; [random > 0.5] { polygon-fill: #adadad; } }');
      expect(this.categoryAutoStyler.getStyle()).toBe(' #layer [\'mapnik::geometry_type\'=3] { polygon-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  }  #layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;   polygon-fill-opacity: 0.9; }');
    });

    it('should escape double quotes correctly', function () {
      var data = this.dataview.get('data');
      data.push({ name: 'Oh"Yeah' });
      this.dataview.set('data', data);
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      this.updateColorsByData();
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#A5AA99"), ("soccer", "basketball", "baseball", "handball", "hockey", "Oh\\"Yeah"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });

    it('should not escape single quotes', function () {
      var data = this.dataview.get('data');
      data.push({ name: "Oh'Yeah" });
      this.dataview.set('data', data);
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      this.updateColorsByData();
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#A5AA99"), ("soccer", "basketball", "baseball", "handball", "hockey", "Oh\'Yeah"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });

    it('should generate proper CartoCSS when Others is included', function () {
      var data = this.dataview.get('data');
      data.push({ name: 'Others', agg: true });
      this.dataview.set('data', data);
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      this.updateColorsByData();
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#A5AA99"), ("soccer", "basketball", "baseball", "handball", "hockey"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });
  });
});
<commit_msg>Removed unnecessary change<commit_after>var specHelper = require('../../spec-helper');
var _ = require('underscore');
var CategoryAutoStyler = require('../../../src/widgets/auto-style/category.js');
var Backbone = require('backbone');

describe('src/widgets/auto-style/category', function () {
  beforeEach(function () {
    var vis = specHelper.createDefaultVis();
    var layer = vis.map.layers.first();
    this.dataview = new Backbone.Model({
      data: [
        { name: 'soccer' },
        { name: 'basketball' },
        { name: 'baseball' },
        { name: 'handball' },
        { name: 'hockey' }
      ],
      column: 'something'
    });

    this.layer = this.dataview.layer = layer;
    this.categoryAutoStyler = new CategoryAutoStyler(this.dataview);

    // Trick to set colors properly in this test
    this.updateColorsByData = function () {
      this.categoryAutoStyler.colors.updateData(_.pluck(this.dataview.get('data'), 'name'));
    }.bind(this);

    this.updateColorsByData();
  });

  describe('.getStyle', function () {
    it('should generate the right styles when layer has polygons', function () {
      this.dataview.layer.set('initialStyle', '#layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;  polygon-fill: #e49115;  polygon-fill-opacity: 0.9; }');
      expect(this.categoryAutoStyler.getStyle()).toBe(' #layer [\'mapnik::geometry_type\'=3] { polygon-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  }  #layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;   polygon-fill-opacity: 0.9; }');
    });

    it('should generate the right styles when layer has points', function () {
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });

    it('should generate the right styles when layer has lines', function () {
      this.dataview.layer.set('initialStyle', '#layer {  line-width: 0.5;  line-color: #fcfafa;  line-opacity: 1; }');
      expect(this.categoryAutoStyler.getStyle()).toBe('#layer [\'mapnik::geometry_type\'=2] { line-color: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  }   #layer {  line-width: 0.5;   line-opacity: 1; }');
    });

    it('should generate unique attr style when layer has multiple attrs', function () {
      this.dataview.layer.set('initialStyle', '#layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;  polygon-fill: #e49115;  polygon-fill-opacity: 0.9; [random > 0.5] { polygon-fill: #adadad; } }');
      expect(this.categoryAutoStyler.getStyle()).toBe(' #layer [\'mapnik::geometry_type\'=3] { polygon-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74"), ("soccer", "basketball", "baseball", "handball", "hockey"));  }  #layer {  polygon-line-width: 0.5;  polygon-line-color: #fcfafa;  polygon-line-opacity: 1;   polygon-fill-opacity: 0.9; }');
    });

    it('should escape double quotes correctly', function () {
      var data = this.dataview.get('data');
      data.push({ name: 'Oh"Yeah' });
      this.dataview.set('data', data);
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      this.updateColorsByData();
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#A5AA99"), ("soccer", "basketball", "baseball", "handball", "hockey", "Oh\\"Yeah"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });

    it('should not escape single quotes', function () {
      var data = this.dataview.get('data');
      data.push({ name: "Oh'Yeah" });
      this.dataview.set('data', data);
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      this.updateColorsByData();
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#A5AA99"), ("soccer", "basketball", "baseball", "handball", "hockey", "Oh\'Yeah"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });

    it('should generate proper CartoCSS when Others is included', function () {
      var data = this.dataview.get('data');
      data.push({ name: 'Others', agg: true });
      this.dataview.set('data', data);
      this.layer.set('initialStyle', '#layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;  marker-fill: #e49115;  marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
      this.updateColorsByData();
      expect(this.categoryAutoStyler.getStyle()).toBe('  #layer [\'mapnik::geometry_type\'=1] { marker-fill: ramp([something], ("#7F3C8D", "#11A579", "#3969AC", "#F2B701", "#E73F74", "#A5AA99"), ("soccer", "basketball", "baseball", "handball", "hockey"));  } #layer {  marker-line-width: 0.5;  marker-line-color: #fcfafa;  marker-line-opacity: 1;  marker-width: 6.076923076923077;   marker-fill-opacity: 0.9;  marker-allow-overlap: true;}');
    });
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import I18n from 'i18n-js';
import PropTypes from 'prop-types';
import Events from '../../../utils/helpers/events';
import DateHelper from '../../../utils/helpers/date';
import DateValidator from '../../../utils/validations/date';
import tagComponent from '../../../utils/helpers/tags';
import DatePicker from './date-picker.component';
import StyledDateInput from './date.style';
import Textbox from '../textbox';
import withUniqueName from '../../../utils/helpers/with-unique-name';

const defaultDateFormat = 'DD/MM/YYYY';

class BaseDateInput extends React.Component {
  isBlurBlocked = false;

  isOpening = false;

  inputHasFocus = this.props.autoFocus || false;

  isControlled = this.props.value !== undefined;

  defaultValue = DateHelper.isValidDate(this.props.value) ? this.props.value : DateHelper.todayFormatted();

  initialVisibleValue = this.isControlled ? this.defaultValue : this.props.defaultValue;

  state = {
    isDatePickerOpen: false,
    /** Date object to pass to the DatePicker */
    selectedDate: DateHelper.stringToDate(this.initialVisibleValue),
    /** Displayed value, format dependent on a region */
    visibleValue: formatDateToCurrentLocale(this.initialVisibleValue)
  };

  componentDidMount() {
    if (this.props.autoFocus) {
      this.isAutoFocused = true;
      this.input.focus();
    }
  }

  componentDidUpdate(prevProps) {
    if (this.isControlled && !this.inputHasFocus && this.hasValueChanged(prevProps)) {
      this.updateVisibleValue(this.props.value);
      this.updateSelectedDate(this.props.value);
    } else if (this.isBlurBlocked && this.hasValueChanged(prevProps)) {
      this.isBlurBlocked = false;
      this.handleBlur();
    }
  }

  hasValueChanged = (prevProps) => {
    return this.props.value && prevProps.value !== this.props.value;
  };

  assignInput = (input) => {
    this.input = input.current;
  };

  handleBlur = () => {
    this.inputHasFocus = false;
    const { disabled, readOnly } = this.props;

    if (disabled || readOnly || this.isBlurBlocked) return;

    this.reformatVisibleDate();
  }

  handleFocus = (ev) => {
    this.inputHasFocus = true;
    const { disabled, readOnly } = this.props;

    if (disabled || readOnly) return;

    if (this.isAutoFocused) {
      this.isAutoFocused = false;
    } else {
      this.openDatePicker();
    }

    if (this.props.onFocus) {
      this.props.onFocus(ev);
    }
  };

  handleTabKeyDown = (ev) => {
    if (Events.isTabKey(ev)) {
      this.isOpening = false;
      this.closeDatePicker();
    }
  };

  openDatePicker = () => {
    this.isBlurBlocked = true;
    document.addEventListener('click', this.closeDatePicker);
    this.updateSelectedDate(this.props.value);
    this.setState({ isDatePickerOpen: true });
  };

  reformatVisibleDate = () => {
    const { visibleValue } = this.state;
    if (DateHelper.isValidDate(visibleValue)) {
      this.setState({ visibleValue: formatDateToCurrentLocale(visibleValue) }, () => {
        if (this.props.onBlur) {
          const dateWithSlashes = DateHelper.sanitizeDateInput(this.input.value);
          const isValidDate = DateHelper.isValidDate(dateWithSlashes);

          if (isValidDate) {
            this.props.onBlur({ target: this.input }, DateHelper.formatValue(dateWithSlashes));
          }
        }
      });
    }
  }

  closeDatePicker = () => {
    if (this.isOpening) {
      this.isOpening = false;
      return;
    }
    document.removeEventListener('click', this.closeDatePicker);
    this.setState({ isDatePickerOpen: false });
  };

  handleDateSelect = (selectedDate) => {
    const stringDateIso = DateHelper.formatDateString(selectedDate);
    this.isBlurBlocked = true;
    this.closeDatePicker();
    this.updateVisibleValue(stringDateIso, true);
  };

  updateVisibleValue = (date, pickerUsed) => {
    const visibleValue = formatDateToCurrentLocale(date);

    this.setState({
      selectedDate: date,
      visibleValue
    }, () => {
      if (pickerUsed) {
        const event = {
          target: this.input
        };
        this.emitOnChangeCallback(event, date);
      }
    });
  };

  handleVisibleInputChange = (ev) => {
    const { disabled, readOnly } = this.props;
    const dateWithSlashes = DateHelper.sanitizeDateInput(ev.target.value);
    const isValidDate = DateHelper.isValidDate(dateWithSlashes);
    let isoDateString;

    if (disabled || readOnly) return;

    this.isBlurBlocked = false;

    if (isValidDate) {
      isoDateString = DateHelper.formatValue(dateWithSlashes);
      this.updateSelectedDate(isoDateString);
      this.emitOnChangeCallback(ev, isoDateString);
    }

    this.setState({ visibleValue: ev.target.value });
  };

  updateSelectedDate = (newValue) => {
    let newDate = DateHelper.stringToDate(newValue);
    const isNewDateInvalid = !newDate.getDate();

    if (isNewDateInvalid) {
      newDate = DateHelper.stringToDate(DateHelper.todayFormatted());
    }

    this.setState({ selectedDate: newDate });
  };

  emitOnChangeCallback = (ev, isoFormattedValue) => {
    if (this.props.onChange) {
      this.props.onChange(ev, isoFormattedValue);
    }
  };

  renderDatePicker = (dateRangeProps) => {
    if (!this.state.isDatePickerOpen) return null;

    return (
      <DatePicker
        inputElement={ this.input && this.input.parentElement }
        selectedDate={ this.state.selectedDate }
        handleDateSelect={ this.handleDateSelect }
        { ...dateRangeProps }
      />
    );
  }

  markCurrentDatepicker = () => {
    this.isOpening = true;
  }

  render() {
    const { minDate, maxDate, ...inputProps } = this.props;
    let events = {};
    delete inputProps.autoFocus;
    delete inputProps.defaultValue;
    delete inputProps.value;

    inputProps.validations = concatAllValidations(inputProps);

    events = {
      onBlur: this.handleBlur,
      onChange: this.handleVisibleInputChange,
      onFocus: this.handleFocus,
      onKeyDown: this.handleTabKeyDown
    };

    return (
      <StyledDateInput
        onClick={ this.markCurrentDatepicker }
        role='presentation'
        size={ inputProps.size }
        { ...tagComponent('date', this.props) }
      >
        <Textbox
          { ...inputProps }
          inputIcon='calendar'
          value={ this.state.visibleValue }
          inputRef={ this.assignInput }
          { ...events }
        />
        {this.renderDatePicker({ minDate, maxDate }) }
      </StyledDateInput>
    );
  }
}

function concatAllValidations(props) {
  if (!props.validations) props.validations = [];
  if (typeof props.validations === 'function') props.validations = [props.validations];

  return [...props.validations, ...props.internalValidations];
}

function formatDateToCurrentLocale(value) {
  const visibleFormat = I18n.t('date.formats.javascript', { defaultValue: defaultDateFormat }).toUpperCase();

  return DateHelper.formatValue(value || DateHelper.todayFormatted(), visibleFormat);
}

const DateInput = withUniqueName(BaseDateInput);

BaseDateInput.propTypes = {
  ...Textbox.propTypes,
  /** Automatically focus on component mount */
  autoFocus: PropTypes.bool,
  /** Used to provide additional validations on composed components */
  internalValidations: PropTypes.array,
  /** Minimum possible date YYYY-MM-DD */
  minDate: PropTypes.string,
  /** Maximum possible date YYYY-MM-DD */
  maxDate: PropTypes.string,
  /** Specify a callback triggered on blur */
  onBlur: PropTypes.func,
  /** Specify a callback triggered on change */
  onChange: PropTypes.func,
  /** Specify a callback triggered on focus */
  onFocus: PropTypes.func,
  /** Name of the input */
  name: PropTypes.string,
  /** The current date YYYY-MM-DD */
  value: PropTypes.string,
  /** Triggers textbox validation when it's boolean value changes */
  forceUpdateTriggerToggle: PropTypes.bool
};

BaseDateInput.defaultProps = {
  internalValidations: [new DateValidator()]
};

export { defaultDateFormat, BaseDateInput };
export default DateInput;
<commit_msg>refactor: removes redundant valid date check<commit_after>import React from 'react';
import I18n from 'i18n-js';
import PropTypes from 'prop-types';
import Events from '../../../utils/helpers/events';
import DateHelper from '../../../utils/helpers/date';
import DateValidator from '../../../utils/validations/date';
import tagComponent from '../../../utils/helpers/tags';
import DatePicker from './date-picker.component';
import StyledDateInput from './date.style';
import Textbox from '../textbox';
import withUniqueName from '../../../utils/helpers/with-unique-name';

const defaultDateFormat = 'DD/MM/YYYY';

class BaseDateInput extends React.Component {
  isBlurBlocked = false;

  isOpening = false;

  inputHasFocus = this.props.autoFocus || false;

  isControlled = this.props.value !== undefined;

  defaultValue = DateHelper.isValidDate(this.props.value) ? this.props.value : DateHelper.todayFormatted();

  initialVisibleValue = this.isControlled ? this.defaultValue : this.props.defaultValue;

  state = {
    isDatePickerOpen: false,
    /** Date object to pass to the DatePicker */
    selectedDate: DateHelper.stringToDate(this.initialVisibleValue),
    /** Displayed value, format dependent on a region */
    visibleValue: formatDateToCurrentLocale(this.initialVisibleValue)
  };

  componentDidMount() {
    if (this.props.autoFocus) {
      this.isAutoFocused = true;
      this.input.focus();
    }
  }

  componentDidUpdate(prevProps) {
    if (this.isControlled && !this.inputHasFocus && this.hasValueChanged(prevProps)) {
      this.updateVisibleValue(this.props.value);
      this.updateSelectedDate(this.props.value);
    } else if (this.isBlurBlocked && this.hasValueChanged(prevProps)) {
      this.isBlurBlocked = false;
      this.handleBlur();
    }
  }

  hasValueChanged = (prevProps) => {
    return this.props.value && prevProps.value !== this.props.value;
  };

  assignInput = (input) => {
    this.input = input.current;
  };

  handleBlur = () => {
    this.inputHasFocus = false;
    const { disabled, readOnly } = this.props;

    if (disabled || readOnly || this.isBlurBlocked) return;

    this.reformatVisibleDate();
  }

  handleFocus = (ev) => {
    this.inputHasFocus = true;
    const { disabled, readOnly } = this.props;

    if (disabled || readOnly) return;

    if (this.isAutoFocused) {
      this.isAutoFocused = false;
    } else {
      this.openDatePicker();
    }

    if (this.props.onFocus) {
      this.props.onFocus(ev);
    }
  };

  handleTabKeyDown = (ev) => {
    if (Events.isTabKey(ev)) {
      this.isOpening = false;
      this.closeDatePicker();
    }
  };

  openDatePicker = () => {
    this.isBlurBlocked = true;
    document.addEventListener('click', this.closeDatePicker);
    this.updateSelectedDate(this.props.value);
    this.setState({ isDatePickerOpen: true });
  };

  reformatVisibleDate = () => {
    const { visibleValue } = this.state;
    if (DateHelper.isValidDate(visibleValue)) {
      this.setState({ visibleValue: formatDateToCurrentLocale(visibleValue) }, () => {
        if (this.props.onBlur) {
          const dateWithSlashes = DateHelper.sanitizeDateInput(visibleValue);
          this.props.onBlur({ target: this.input }, DateHelper.formatValue(dateWithSlashes));
        }
      });
    }
  }

  closeDatePicker = () => {
    if (this.isOpening) {
      this.isOpening = false;
      return;
    }
    document.removeEventListener('click', this.closeDatePicker);
    this.setState({ isDatePickerOpen: false });
  };

  handleDateSelect = (selectedDate) => {
    const stringDateIso = DateHelper.formatDateString(selectedDate);
    this.isBlurBlocked = true;
    this.closeDatePicker();
    this.updateVisibleValue(stringDateIso, true);
  };

  updateVisibleValue = (date, pickerUsed) => {
    const visibleValue = formatDateToCurrentLocale(date);

    this.setState({
      selectedDate: date,
      visibleValue
    }, () => {
      if (pickerUsed) {
        const event = {
          target: this.input
        };
        this.emitOnChangeCallback(event, date);
      }
    });
  };

  handleVisibleInputChange = (ev) => {
    const { disabled, readOnly } = this.props;
    const dateWithSlashes = DateHelper.sanitizeDateInput(ev.target.value);
    const isValidDate = DateHelper.isValidDate(dateWithSlashes);
    let isoDateString;

    if (disabled || readOnly) return;

    this.isBlurBlocked = false;

    if (isValidDate) {
      isoDateString = DateHelper.formatValue(dateWithSlashes);
      this.updateSelectedDate(isoDateString);
      this.emitOnChangeCallback(ev, isoDateString);
    }

    this.setState({ visibleValue: ev.target.value });
  };

  updateSelectedDate = (newValue) => {
    let newDate = DateHelper.stringToDate(newValue);
    const isNewDateInvalid = !newDate.getDate();

    if (isNewDateInvalid) {
      newDate = DateHelper.stringToDate(DateHelper.todayFormatted());
    }

    this.setState({ selectedDate: newDate });
  };

  emitOnChangeCallback = (ev, isoFormattedValue) => {
    if (this.props.onChange) {
      this.props.onChange(ev, isoFormattedValue);
    }
  };

  renderDatePicker = (dateRangeProps) => {
    if (!this.state.isDatePickerOpen) return null;

    return (
      <DatePicker
        inputElement={ this.input && this.input.parentElement }
        selectedDate={ this.state.selectedDate }
        handleDateSelect={ this.handleDateSelect }
        { ...dateRangeProps }
      />
    );
  }

  markCurrentDatepicker = () => {
    this.isOpening = true;
  }

  render() {
    const { minDate, maxDate, ...inputProps } = this.props;
    let events = {};
    delete inputProps.autoFocus;
    delete inputProps.defaultValue;
    delete inputProps.value;

    inputProps.validations = concatAllValidations(inputProps);

    events = {
      onBlur: this.handleBlur,
      onChange: this.handleVisibleInputChange,
      onFocus: this.handleFocus,
      onKeyDown: this.handleTabKeyDown
    };

    return (
      <StyledDateInput
        onClick={ this.markCurrentDatepicker }
        role='presentation'
        size={ inputProps.size }
        { ...tagComponent('date', this.props) }
      >
        <Textbox
          { ...inputProps }
          inputIcon='calendar'
          value={ this.state.visibleValue }
          inputRef={ this.assignInput }
          { ...events }
        />
        {this.renderDatePicker({ minDate, maxDate }) }
      </StyledDateInput>
    );
  }
}

function concatAllValidations(props) {
  if (!props.validations) props.validations = [];
  if (typeof props.validations === 'function') props.validations = [props.validations];

  return [...props.validations, ...props.internalValidations];
}

function formatDateToCurrentLocale(value) {
  const visibleFormat = I18n.t('date.formats.javascript', { defaultValue: defaultDateFormat }).toUpperCase();

  return DateHelper.formatValue(value || DateHelper.todayFormatted(), visibleFormat);
}

const DateInput = withUniqueName(BaseDateInput);

BaseDateInput.propTypes = {
  ...Textbox.propTypes,
  /** Automatically focus on component mount */
  autoFocus: PropTypes.bool,
  /** Used to provide additional validations on composed components */
  internalValidations: PropTypes.array,
  /** Minimum possible date YYYY-MM-DD */
  minDate: PropTypes.string,
  /** Maximum possible date YYYY-MM-DD */
  maxDate: PropTypes.string,
  /** Specify a callback triggered on blur */
  onBlur: PropTypes.func,
  /** Specify a callback triggered on change */
  onChange: PropTypes.func,
  /** Specify a callback triggered on focus */
  onFocus: PropTypes.func,
  /** Name of the input */
  name: PropTypes.string,
  /** The current date YYYY-MM-DD */
  value: PropTypes.string,
  /** Triggers textbox validation when it's boolean value changes */
  forceUpdateTriggerToggle: PropTypes.bool
};

BaseDateInput.defaultProps = {
  internalValidations: [new DateValidator()]
};

export { defaultDateFormat, BaseDateInput };
export default DateInput;
<|endoftext|>


<commit_before>// Easify.JS
// Author: Sam Webb
// Copyright: 2015
// License: MIT
// Version: 0.0.4

// Dependencies: N/A

// Start with semicolon in case other libraries
// don't end with one.
;(function(global){
  // Creating new easify object:
  // var e = Easify();
  // or the preferred method
  // var e = $E();
  // Will be using e variable throughout the code

  // Variables
  var validateString,
      validateNum,
      randomNumberFromItemLength;

  // Private Functions

  // Returns a random number from 0 to the length of
  // the passed in array or string
  randomNumberFromItemLength = function(item) {
    return Math.floor(Math.random() * item.length)
  }

  // For validating strings
  // Always use this if method takes a string artument
  validateString = function(str) {
    // If str can be coerced to false, it will fail
    if (!str) {
      return false;
    }
    // If str is not of type "string" it will fail
    if (typeof str === "string") {
      return true;
    } else {
      return false;
    }
  }

  // For validating numbers
  // Always use this if method takes a number argument
  validateNum = function(num) {
    // If num is not of type "number" it will fail
    if(typeof num !== "number") {
      return false;
    }
    return true;
  }

  // End of private functions

  // 'new' Easify object
  var Easify = function() {
    return new Easify.init();
  }

  Easify.VERSION = '0.0.3';

  // Function that actually creates object
  // to remove 'new' keyword for users
  Easify.init = function() {
    var self = this;
  }

  // Universal methods and properties of Easify
  // on the protorype
  Easify.prototype = {

    // **************
    // **************
    // STRING METHODS
    // **************
    // **************

    capitalize: function(str) {
      if (validateString(str)) {
        var strList = str.split('');
        strList[0] = strList[0].toUpperCase();
        str = strList.join('');
        return str;
      } else {
        throw 'String validation failed.'
      }
    },

    isString: function(str) {
      return validateString(str);
    },

    // Easy way to grab the last letter of a string
    //
    // Takes 1 argument(string)
    // e.last('hello'); //=> "o"
    last: function(str) {
      if (validateString(str)) {
        return str[str.length - 1];
      } else {
        throw 'String validation failed.'
      }
    },

    // Randomly removes a specified amount of characters from
    // a string
    //
    // Takes 2 arguments(string, number)
    // e.remove('hello', 2); //=> 'hlo'
    // Honestly can't see a use case for this but it's here
    remove: function(str, amount) {
      if (validateString(str) && validateNum(amount)){
        // Protects amount from being larger than the string
        // argument's length
        if (amount > str.length) {
          amount = str.length;
        }
        // Split the string into an array
        var arr = str.split('');
        // Set some variables
        var newString = '';
        var num;
        // Iterate to remove each letter
        for (var i = 0; i < amount; i++) {
          // Set random number from 0 to 1 below array length
          num = randomNumberFromItemLength(arr);
          // Remove random letter from the array
          arr.splice(num, 1)[0];
        }
        newString = arr.join('');
        return newString;
      } else {
        throw 'Argument validation failed.';
      }

    },

    // Removes all occurances of the specified letter from
    // the string
    //
    // Takes 2 arguments(string, string)
    // e.removeAll('hello', 'l'); //=> 'heo'
    removeAll: function(str, letter) {
      if (validateString(str) && validateString(letter)) {
        // Makes sure the letter argument is 1 character long
        if (letter.length !== 1) {
          throw 'The letter argument should not be greater or less than 1.'
        }
        // Sets some variables.
        var newString;
        var arr = str.split('');
        // Loops over the array to check the letters
        for (var i = 0; i < arr.length; i++) {
          // If the letter in the array matches the letter
          // argument, this will run
          if (arr[i] === letter) {
            // Removes letter
            arr.splice(i, 1);
            // Small code to make sure when something is removed,
            // the loop doesn't skip the next letter
            i = i - 1;
          }
        }
        // Join the array back together
        newString = arr.join('');
        return newString;
      } else {
        throw 'String validation failed.'
      }
    },

    // Returns a randomized version of a passed in string
    //
    // Takes 1 argument(string)
    // e.randomize("Hello"); //=> "olelH"
    randomize: function(str) {
      if (validateString(str)) {
        // Split the string into an array
        var arr = str.split('');
        // Set some variables
        var newString = '';
        var num;
        // Array will get shortened by 1 for each iteration
        while (arr.length > 0) {
          // Set random number from 0 to 1 below array length
          num = randomNumberFromItemLength(arr);
          // Add letter to newString
          newString += arr[num];
          // Remove the letter from the array
          arr.splice(num, 1);
        }
        return newString;
      } else {
        throw 'String validation failed.'
      }
    },

    // Repeat the same string multiple times, trimming off
    // extra white space at the beginning or end
    //
    // Takes 2 arguments(string, number)
    // e.repeat('hello ', 4); //=> "hello hello hello hello"
    repeat: function(str, count) {
      if(validateString(str) && validateNum(count)) {
        // Variable to store the created string
        var repeatedString = '';
        // Add input string to the variable however many
        // times, specified in count variable.
        // If count is 0, an empty string will be returned.
        for (var i = 0; i < count; i++) {
          repeatedString += str;
        }
        return repeatedString.trim();
      } else {
        throw 'Argument validation failed.'
      }
    },

    // Reverse a string
    //
    // Takes 1 argument(string)
    // e.reverse('hello'); //=> 'olleh'
    reverse: function(str) {
      if(validateString(str)) {
        // Break string up into an array of characters
        // including spaces.
        var arr = str.split('');
        // Reverse array
        arr = arr.reverse();
        // Rejoin array into string
        str = arr.join('');
        return str;
      } else {
        throw 'String validation failed.'
      }
    },

    // Wrap a string in a specified element
    //
    // Takes 2 arguments(string, string)
    // e.wrap('Hello', 'h1'); //=> "
Hello
"
    wrap: function(str, element) {
      // String validation
      if (validateString(str) && validateString(element)) {
        return '<' + element + '>' + str + '</' + element + '>';
      } else {
        throw 'String validation failed.'
      }
    },

    // *************
    // *************
    // ARRAY METHODS
    // *************
    // *************

    // Combines 2 arrays into a single array with all the values
    bridge: function(arr1, arr2) {
      if (this.isArray(arr1) && this.isArray(arr2)) {
        for (var i = 0; i < arr2.length; i++) {
          arr1.push(arr2[i]);
        }
        return arr1;
      }
    },

    // Checks the type of each element contained in the passed
    // in array
    //
    // Takes 1 argument(array)
    // e.checkTypes([1, 'hello', [], {}, function(){}, true])
    // returns
    // ['number', 'string', 'array', 'object', 'function', 'boolean']
    checkTypes: function(arr) {
      if (Array.isArray(arr) === true) {
        var returnedArray = [];
        for (var i = 0; i < arr.length; i++) {
          // Method depends on checkType() method
          returnedArray.push(this.checkType(arr[i]));
        }
        return returnedArray;
      }
      throw 'checkTypes() requires an array argument'
    },

    // Returns true if the passed in value is inside of the array
    contains: function(arr, value) {
      if (Array.isArray(arr) === true) {
        var isIn = false;
        for (var i = 0; i < arr.length; i++) {
          if (arr[i] === value) {
            isIn = true;
          }
        }
        return isIn;
      }
      throw 'contains() requires an array argument'
    },

    // Checks if input value is an array
    isArray: function(arr) {
      if (Array.isArray(arr) === true) {
        return true;
      } else {
        return false;
      }
    },

    // **************
    // **************
    // OBJECT METHODS
    // **************
    // **************

    // Checks if input value returns 'object' and not 'array'
    isObject: function(obj) {
      if (Array.isArray(obj) === true) {
        return false;
      } else if (obj === null) {
        return false;
      } else if (typeof obj === 'object') {
        return true;
      } else {
        return false;
      }
    },

    // ***********
    // ***********
    // DOM METHODS
    // ***********
    // ***********

    // Insert HTML into the selected HTML element
    //
    // Takes 4 arguments(string, string, string, number)
    // e.insertHTML('id', 'home-link', 'Home');
    // The previous code will insert the text 'Home' inside
    // of the element on your page with an id of #home-link.
    // Any valid HTML can also be be the input value.
    //
    // Arguments
    // ----------
    // The selectorType argument is expecting a string of
    // either 'id' or 'class'
    //
    // The selector artument is expecting a string that
    // corrisponds to the name provided in the HTML
    //
    // The input argument can be any valid HTML or text
    //
    // The amount argument is an optional argument and is
    // used when a selector which returns multiple elements
    // is selected. You can set an amount of elements to
    // add your HTML to. It will go in order starting from
    // the first element. If no amount is selected, all
    // elements will get the change.
    // ----------
    insertHTML: function(selectorType, selector, input, amount) {
      // Makes sure first 3 arguments valid
      if (validateString(selectorType) && validateString(selector) && validateString(input)) {

        // Sets some variables
        var element;
        var elements = [];
        var num = 0;

        // Checking the selector type for 'id'
        if (selectorType.toLowerCase() === "id") {
          element = document.getElementById(selector);
          element.innerHTML = input;
        // Checking the selector type of 'class'
        } else if (selectorType.toLowerCase() === "class") {
          // If amount validates to be a number, set the num
          // variable to amount
          if (validateNum(amount)) { num = amount }
          elements = document.getElementsByClassName(selector);
          // Iterates over the elements array and adds the html
          // to every element contained in it
          for (var i = 0; i < ( amount || elements.length ); i++) {
            // Prevents error if amount is higher than the length of
            // elements
            if (i === elements.length) {
              break;
            }
            element = elements[i];
            element.innerHTML = input;
          }
        } else {
          throw 'You must pass either "id" or "class" as the selectorType argument to insertHTML function.'
        }
        // Returns true if successful
        return true;
      }
      // Returns false if unsuccessful
      throw 'insertHTML arguments are invalid.'
      return false;
      // Does not check if there is a matching HTML element. YET!
    },

    // Returns the DOM element with the passed in ID
    elementFromId: function(id) {
      return document.getElementById(id);
    },

    // Returns a list of DOM elements with the passed in class
    elementsFromClass: function(classname) {
      return document.getElementsByClassName(classname);
    },

    // Returns a list of DOM elements with the passed in tag
    elementsFromTag: function(tag) {
      return document.getElementsByTagName(tag);
    },

    elementsFromName: function(name) {
      return document.getElementsByName(name);
    },

    // *****************
    // *****************
    // UNIVERSAL METHODS
    // *****************
    // *****************

    // Returns true if arguments are equal.
    isEqual: function(a, b) {
      return a === b;
    },

    // Returns true if arguments are not equal.
    isNotEqual: function(a, b) {
      return a !== b;
    },

    // Returns true if arguments can be coerced to equal.
    isSimilar: function(a, b) {
      return a == b;
    },

    // Returns true if arguments can not be coreced to equal.
    isNotSimilar: function(a, b) {
      return a != b;
    },

    // Checks if the argument is a truthy value
    isTruthy: function(a) {
      if (a) {
        return true;
      } else {
        return false;
      }
    },

    // Checks if the argument is a falsey value
    isFalsey: function(a) {
      if(!a) {
        return true;
      } else {
        return false;
      }
    },

    // Runs callback if comparison returns true
    ifTrue: function(comparison, callback){
      if(comparison) {
        callback();
      }
    },

    // Runs callback if comparison returns false
    ifFalse: function(comparison, callback) {
      if(!comparison) {
        callback();
      }
    },

    // Checks the type of a passed in value
    checkType: function(a) {
      // Specific check for arrays since they return from
      // typeof as 'object'
      if (Array.isArray(a) === true) {
        return 'array';
      // Check for null since null returns from typeof as
      // 'object'
      } else if (a === null) {
        return 'null';
      } {
        // Checks for everything else with typeof
        switch (typeof a) {
          case 'string':
            return 'string';
          case 'number':
            return 'number';
          case 'boolean':
            return 'boolean';
          case 'function':
            return 'function';
          case 'object':
            return 'object';
          case 'undefined':
            return 'undefined';
          default:
            return null;
        }
      }
    },

    // Returns an array containing all Easify methods
    methods: function() {
      return Object.keys($E.prototype);
    },

    // Returns the amount of Easify methods
    methodCount: function() {
      return this.methods().length;
    }

  }

  // **********
  // **********
  // ALTERNATES
  // **********
  // **********

  // Alternate name for isEqual method
  Easify.prototype.equals = Easify.prototype.isEqual;

  // ******************************

  // Point init prototype to the Easify prototype
  Easify.init.prototype = Easify.prototype;

  // Sets up global variables
  global.Easify = global.$E = Easify

  console.log("Easify loaded!");
}(window));<commit_msg>refactored array code<commit_after>// Easify.JS
// Author: Sam Webb
// Copyright: 2015
// License: MIT
// Version: 0.1.0

// Dependencies: N/A

// Start with semicolon in case other libraries
// don't end with one.
;(function(global){
  // Creating new easify object:
  // var e = Easify();
  // or the preferred method
  // var e = $E();
  // Will be using e variable throughout the code

  // Variables
  var validateArray,
      validateString,
      validateNum,
      randomNumberFromItemLength;

  // Private Functions

  // Returns a random number from 0 to the length of
  // the passed in array or string
  randomNumberFromItemLength = function(item) {
    return Math.floor(Math.random() * item.length)
  }

  validateArray = function(arr) {
    if (Array.isArray(arr) === true) {
      return true;
    }
    return false;
  }

  // For validating strings
  // Always use this if method takes a string artument
  validateString = function(str) {
    // If str can be coerced to false, it will fail
    if (!str) {
      return false;
    }
    // If str is not of type "string" it will fail
    if (typeof str === "string") {
      return true;
    } else {
      return false;
    }
  }

  // For validating numbers
  // Always use this if method takes a number argument
  validateNum = function(num) {
    // If num is not of type "number" it will fail
    if(typeof num !== "number") {
      return false;
    }
    return true;
  }

  // End of private functions

  // 'new' Easify object
  var Easify = function() {
    return new Easify.init();
  }

  Easify.VERSION = '0.1.0';

  // Function that actually creates object
  // to remove 'new' keyword for users
  Easify.init = function() {
    var self = this;
  }

  // Universal methods and properties of Easify
  // on the protorype
  Easify.prototype = {

    // **************
    // **************
    // STRING METHODS
    // **************
    // **************

    capitalize: function(str) {
      if (validateString(str)) {
        var strList = str.split('');
        strList[0] = strList[0].toUpperCase();
        str = strList.join('');
        return str;
      } else {
        throw 'String validation failed.'
      }
    },

    isString: function(str) {
      return validateString(str);
    },

    // Easy way to grab the last letter of a string
    //
    // Takes 1 argument(string)
    // e.last('hello'); //=> "o"
    last: function(str) {
      if (validateString(str)) {
        return str[str.length - 1];
      } else {
        throw 'String validation failed.'
      }
    },

    // Randomly removes a specified amount of characters from
    // a string
    //
    // Takes 2 arguments(string, number)
    // e.remove('hello', 2); //=> 'hlo'
    // Honestly can't see a use case for this but it's here
    remove: function(str, amount) {
      if (validateString(str) && validateNum(amount)){
        // Protects amount from being larger than the string
        // argument's length
        if (amount > str.length) {
          amount = str.length;
        }
        // Split the string into an array
        var arr = str.split('');
        // Set some variables
        var newString = '';
        var num;
        // Iterate to remove each letter
        for (var i = 0; i < amount; i++) {
          // Set random number from 0 to 1 below array length
          num = randomNumberFromItemLength(arr);
          // Remove random letter from the array
          arr.splice(num, 1)[0];
        }
        newString = arr.join('');
        return newString;
      } else {
        throw 'Argument validation failed.';
      }

    },

    // Removes all occurances of the specified letter from
    // the string
    //
    // Takes 2 arguments(string, string)
    // e.removeAll('hello', 'l'); //=> 'heo'
    removeAll: function(str, letter) {
      if (validateString(str) && validateString(letter)) {
        // Makes sure the letter argument is 1 character long
        if (letter.length !== 1) {
          throw 'The letter argument should not be greater or less than 1.'
        }
        // Sets some variables.
        var newString;
        var arr = str.split('');
        // Loops over the array to check the letters
        for (var i = 0; i < arr.length; i++) {
          // If the letter in the array matches the letter
          // argument, this will run
          if (arr[i] === letter) {
            // Removes letter
            arr.splice(i, 1);
            // Small code to make sure when something is removed,
            // the loop doesn't skip the next letter
            i = i - 1;
          }
        }
        // Join the array back together
        newString = arr.join('');
        return newString;
      } else {
        throw 'String validation failed.'
      }
    },

    // Returns a randomized version of a passed in string
    //
    // Takes 1 argument(string)
    // e.randomize("Hello"); //=> "olelH"
    randomize: function(str) {
      if (validateString(str)) {
        // Split the string into an array
        var arr = str.split('');
        // Set some variables
        var newString = '';
        var num;
        // Array will get shortened by 1 for each iteration
        while (arr.length > 0) {
          // Set random number from 0 to 1 below array length
          num = randomNumberFromItemLength(arr);
          // Add letter to newString
          newString += arr[num];
          // Remove the letter from the array
          arr.splice(num, 1);
        }
        return newString;
      } else {
        throw 'String validation failed.'
      }
    },

    // Repeat the same string multiple times, trimming off
    // extra white space at the beginning or end
    //
    // Takes 2 arguments(string, number)
    // e.repeat('hello ', 4); //=> "hello hello hello hello"
    repeat: function(str, count) {
      if(validateString(str) && validateNum(count)) {
        // Variable to store the created string
        var repeatedString = '';
        // Add input string to the variable however many
        // times, specified in count variable.
        // If count is 0, an empty string will be returned.
        for (var i = 0; i < count; i++) {
          repeatedString += str;
        }
        return repeatedString.trim();
      } else {
        throw 'Argument validation failed.'
      }
    },

    // Reverse a string
    //
    // Takes 1 argument(string)
    // e.reverse('hello'); //=> 'olleh'
    reverse: function(str) {
      if(validateString(str)) {
        // Break string up into an array of characters
        // including spaces.
        var arr = str.split('');
        // Reverse array
        arr = arr.reverse();
        // Rejoin array into string
        str = arr.join('');
        return str;
      } else {
        throw 'String validation failed.'
      }
    },

    // Wrap a string in a specified element
    //
    // Takes 2 arguments(string, string)
    // e.wrap('Hello', 'h1'); //=> "
Hello
"
    wrap: function(str, element) {
      // String validation
      if (validateString(str) && validateString(element)) {
        return '<' + element + '>' + str + '</' + element + '>';
      } else {
        throw 'String validation failed.'
      }
    },

    // *************
    // *************
    // ARRAY METHODS
    // *************
    // *************

    // Combines 2 arrays into a single array with all the values
    bridge: function(arr1, arr2) {
      if (validateArray(arr1) && validateArray(arr2)) {
        for (var i = 0; i < arr2.length; i++) {
          arr1.push(arr2[i]);
        }
        return arr1;
      }
    },

    // Checks the type of each element contained in the passed
    // in array
    //
    // Takes 1 argument(array)
    // e.checkTypes([1, 'hello', [], {}, function(){}, true])
    // returns
    // ['number', 'string', 'array', 'object', 'function', 'boolean']
    checkTypes: function(arr) {
      if (validateArray(arr)) {
        var returnedArray = [];
        for (var i = 0; i < arr.length; i++) {
          // Method depends on checkType() method
          returnedArray.push(this.checkType(arr[i]));
        }
        return returnedArray;
      }
      throw 'checkTypes() requires an array argument'
    },

    // Returns true if the passed in value is inside of the array
    contains: function(arr, value) {
      if (validateArray(arr)) {
        var isIn = false;
        for (var i = 0; i < arr.length; i++) {
          if (arr[i] === value) {
            isIn = true;
          }
        }
        return isIn;
      }
      throw 'contains() requires an array argument'
    },

    // Checks if input value is an array
    isArray: function(arr) {
      if (validateArray(arr)) {
        return true;
      } else {
        return false;
      }
    },

    // **************
    // **************
    // OBJECT METHODS
    // **************
    // **************

    // Checks if input value returns 'object' and not 'array'
    isObject: function(obj) {
      if (validateArray(obj)) {
        return false;
      } else if (obj === null) {
        return false;
      } else if (typeof obj === 'object') {
        return true;
      } else {
        return false;
      }
    },

    // ***********
    // ***********
    // DOM METHODS
    // ***********
    // ***********

    // Insert HTML into the selected HTML element
    //
    // Takes 4 arguments(string, string, string, number)
    // e.insertHTML('id', 'home-link', 'Home');
    // The previous code will insert the text 'Home' inside
    // of the element on your page with an id of #home-link.
    // Any valid HTML can also be be the input value.
    //
    // Arguments
    // ----------
    // The selectorType argument is expecting a string of
    // either 'id' or 'class'
    //
    // The selector artument is expecting a string that
    // corrisponds to the name provided in the HTML
    //
    // The input argument can be any valid HTML or text
    //
    // The amount argument is an optional argument and is
    // used when a selector which returns multiple elements
    // is selected. You can set an amount of elements to
    // add your HTML to. It will go in order starting from
    // the first element. If no amount is selected, all
    // elements will get the change.
    // ----------
    insertHTML: function(selectorType, selector, input, amount) {
      // Makes sure first 3 arguments valid
      if (validateString(selectorType) && validateString(selector) && validateString(input)) {

        // Sets some variables
        var element;
        var elements = [];
        var num = 0;

        // Checking the selector type for 'id'
        if (selectorType.toLowerCase() === "id") {
          element = document.getElementById(selector);
          element.innerHTML = input;
        // Checking the selector type of 'class'
        } else if (selectorType.toLowerCase() === "class") {
          // If amount validates to be a number, set the num
          // variable to amount
          if (validateNum(amount)) { num = amount }
          elements = document.getElementsByClassName(selector);
          // Iterates over the elements array and adds the html
          // to every element contained in it
          for (var i = 0; i < ( amount || elements.length ); i++) {
            // Prevents error if amount is higher than the length of
            // elements
            if (i === elements.length) {
              break;
            }
            element = elements[i];
            element.innerHTML = input;
          }
        } else {
          throw 'You must pass either "id" or "class" as the selectorType argument to insertHTML function.'
        }
        // Returns true if successful
        return true;
      }
      // Returns false if unsuccessful
      throw 'insertHTML arguments are invalid.'
      return false;
      // Does not check if there is a matching HTML element. YET!
    },

    // Returns the DOM element with the passed in ID
    elementFromId: function(id) {
      return document.getElementById(id);
    },

    // Returns a list of DOM elements with the passed in class
    elementsFromClass: function(classname) {
      return document.getElementsByClassName(classname);
    },

    // Returns a list of DOM elements with the passed in tag
    elementsFromTag: function(tag) {
      return document.getElementsByTagName(tag);
    },

    elementsFromName: function(name) {
      return document.getElementsByName(name);
    },

    // *****************
    // *****************
    // UNIVERSAL METHODS
    // *****************
    // *****************

    // Returns true if arguments are equal.
    isEqual: function(a, b) {
      return a === b;
    },

    // Returns true if arguments are not equal.
    isNotEqual: function(a, b) {
      return a !== b;
    },

    // Returns true if arguments can be coerced to equal.
    isSimilar: function(a, b) {
      return a == b;
    },

    // Returns true if arguments can not be coreced to equal.
    isNotSimilar: function(a, b) {
      return a != b;
    },

    // Checks if the argument is a truthy value
    isTruthy: function(a) {
      if (a) {
        return true;
      } else {
        return false;
      }
    },

    // Checks if the argument is a falsey value
    isFalsey: function(a) {
      if(!a) {
        return true;
      } else {
        return false;
      }
    },

    // Runs callback if comparison returns true
    ifTrue: function(comparison, callback){
      if(comparison) {
        callback();
      }
    },

    // Runs callback if comparison returns false
    ifFalse: function(comparison, callback) {
      if(!comparison) {
        callback();
      }
    },

    // Checks the type of a passed in value
    checkType: function(a) {
      // Specific check for arrays since they return from
      // typeof as 'object'
      if (Array.isArray(a) === true) {
        return 'array';
      // Check for null since null returns from typeof as
      // 'object'
      } else if (a === null) {
        return 'null';
      } {
        // Checks for everything else with typeof
        switch (typeof a) {
          case 'string':
            return 'string';
          case 'number':
            return 'number';
          case 'boolean':
            return 'boolean';
          case 'function':
            return 'function';
          case 'object':
            return 'object';
          case 'undefined':
            return 'undefined';
          default:
            return null;
        }
      }
    },

    // Returns an array containing all Easify methods
    methods: function() {
      return Object.keys($E.prototype);
    },

    // Returns the amount of Easify methods
    methodCount: function() {
      return this.methods().length;
    }

  }

  // **********
  // **********
  // ALTERNATES
  // **********
  // **********

  // Alternate name for isEqual method
  Easify.prototype.equals = Easify.prototype.isEqual;

  // ******************************

  // Point init prototype to the Easify prototype
  Easify.init.prototype = Easify.prototype;

  // Sets up global variables
  global.Easify = global.$E = Easify

  console.log("Easify loaded!");
}(window));<|endoftext|>


<commit_before>//  Copyright (c) 2011-2012 UX Productivity Pty Ltd. All rights reserved.

var backMessages = new Array();

function getBackMessages()
{
    var result = JSON.stringify(backMessages);
    backMessages = new Array();
    return result;
}

function addBackMessage()
{
    backMessages.push(arrayCopy(arguments));
    return null;
}

var Editor = {
    debug: function(str)
    {
        addBackMessage("debug",str);
    },

    setOutline: function(outline)
    {
        addBackMessage("setOutline",outline);
    },

    setStyles: function(styles)
    {
        addBackMessage("setStyles",styles);
    },

    setCursor: function(x,y,width,height)
    {
        addBackMessage("setCursor",x,y,width,height);
    },

    setSelectionHandles: function(x1,y1,height1,x2,y2,height2)
    {
        addBackMessage("setSelectionHandles",x1,y1,height1,x2,y2,height2);
    },

    clearSelectionHandlesAndCursor: function()
    {
        addBackMessage("clearSelectionHandlesAndCursor");
    },

    setSelectionBounds: function(left,top,right,bottom)
    {
        addBackMessage("setSelectionBounds",left,top,right,bottom);
    },

    reportJSError: function(error)
    {
        addBackMessage("reportJSError",error.toString());
    },
}

function debug(str)
{
    Editor.debug(str);
}

function warning(str)
{
    debug("WARNING: "+str);
}

//function showProperties(object,objname)
//{
//    var functions = new Array();
//    var properties = new Array();
//    for (var name in object) {
//        if (typeof(object[name]) == "function")
//            functions.push(name);
//        else
//            properties.push(name);
//    }
//    functions.sort();
//    properties.sort();
//    for (var i = 0; i < functions.length; i++)
//        debug(objname+"."+functions[i]+"()");
//    for (var i = 0; i < properties.length; i++)
//        debug(objname+"."+properties[i]);
//}
<commit_msg>Wrapped the Editor definitions in an anonymous function scope, so that the backMessages array and addBackMessage() functions are private.<commit_after>//  Copyright (c) 2011-2012 UX Productivity Pty Ltd. All rights reserved.

(function(){

    var backMessages = new Array();

    function addBackMessage()
    {
        backMessages.push(arrayCopy(arguments));
        return null;
    }

    window.Editor = {

        getBackMessages: function()
        {
            var result = JSON.stringify(backMessages);
            backMessages = new Array();
            return result;
        },

        debug: function(str)
        {
            addBackMessage("debug",str);
        },

        setOutline: function(outline)
        {
            addBackMessage("setOutline",outline);
        },

        setStyles: function(styles)
        {
            addBackMessage("setStyles",styles);
        },

        setCursor: function(x,y,width,height)
        {
            addBackMessage("setCursor",x,y,width,height);
        },

        setSelectionHandles: function(x1,y1,height1,x2,y2,height2)
        {
            addBackMessage("setSelectionHandles",x1,y1,height1,x2,y2,height2);
        },

        clearSelectionHandlesAndCursor: function()
        {
            addBackMessage("clearSelectionHandlesAndCursor");
        },

        setSelectionBounds: function(left,top,right,bottom)
        {
            addBackMessage("setSelectionBounds",left,top,right,bottom);
        },

        reportJSError: function(error)
        {
            addBackMessage("reportJSError",error.toString());
        },
    }

    window.debug = function(str)
    {
        Editor.debug(str);
    }
})();
<|endoftext|>