Initial Commit
diff --git a/src/JSONLoader.js b/src/JSONLoader.js
new file mode 100644
index 0000000..10e2840
--- /dev/null
+++ b/src/JSONLoader.js
@@ -0,0 +1,26 @@
+module.exports = function JSONLoader(){
+ var self = this;
+
+ function receivedResponse(xhr){
+ return xhr.status==200 && xhr.readyState==4;
+ }
+
+ function handleResponse(xhr,callback){
+ xhr.onreadystatechange = function(){
+ if ( receivedResponse(xhr) ){
+ try{
+ callback(null,JSON.parse(xhr.responseText) );
+ }catch(err){
+ callback(err,null);
+ }
+ }
+ }
+ }
+
+ self.load = function(location,callback){
+ var xhr = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
+ xhr.open("GET", location, true);
+ handleResponse(xhr,callback);
+ xhr.send();
+ };
+};
\ No newline at end of file
diff --git a/src/SearchStrategies/fuzzy.js b/src/SearchStrategies/fuzzy.js
new file mode 100644
index 0000000..ab26035
--- /dev/null
+++ b/src/SearchStrategies/fuzzy.js
@@ -0,0 +1,15 @@
+module.exports = new FuzzySearchStrategy();
+
+function FuzzySearchStrategy(){
+ var self = this;
+
+ function createFuzzyRegExpFromString(string){
+ return new RegExp( string.split('').join('.*?'), 'gi');
+ }
+
+ self.matches = function(string,crit){
+ if( typeof string !== 'string' ) return false;
+ string = string.trim();
+ return !!string.match(createFuzzyRegExpFromString(crit));
+ };
+};
\ No newline at end of file
diff --git a/src/SearchStrategies/literal.js b/src/SearchStrategies/literal.js
new file mode 100644
index 0000000..9bddbe9
--- /dev/null
+++ b/src/SearchStrategies/literal.js
@@ -0,0 +1,15 @@
+module.exports = new LiteralSearchStrategy();
+
+function LiteralSearchStrategy(){
+ var self = this;
+
+ function doMatch(string,crit){
+ return string.toLowerCase().indexOf(crit.toLowerCase()) >= 0;
+ }
+
+ self.matches = function(string,crit){
+ if( typeof string !== 'string' ) return false;
+ string = string.trim();
+ return doMatch(string,crit);
+ };
+};
\ No newline at end of file
diff --git a/src/Searcher.js b/src/Searcher.js
new file mode 100644
index 0000000..99626dd
--- /dev/null
+++ b/src/Searcher.js
@@ -0,0 +1,42 @@
+module.exports = function Searcher(){
+ var self = this;
+
+ var matches = [];
+
+ var fuzzy = false;
+ var limit = 10;
+
+ var fuzzySearchStrategy = require('./SearchStrategies/fuzzy');
+ var literalSearchStrategy = require('./SearchStrategies/literal');
+
+ self.setFuzzy = function(_fuzzy){ fuzzy = !!_fuzzy; };
+
+ self.setLimit = function(_limit){ limit = parseInt(_limit,10) || limit; };
+
+ self.search = function(data,crit){
+ if( !crit ) return [];
+ matches.length = 0;
+ return findMatches(data,crit,getSearchStrategy());
+ };
+
+ function findMatches(store,crit,strategy){
+ var data = store.get();
+ for(var i = 0; i < data.length && matches.length < limit; i++) {
+ findMatchesInObject(data[i],crit,strategy);
+ }
+ return matches;
+ }
+
+ function findMatchesInObject(obj,crit,strategy){
+ for(var key in obj) {
+ if( strategy.matches(obj[key], crit) ){
+ matches.push(obj);
+ break;
+ }
+ }
+ }
+
+ function getSearchStrategy(){
+ return fuzzy ? fuzzySearchStrategy : literalSearchStrategy;
+ }
+};
\ No newline at end of file
diff --git a/src/Store.js b/src/Store.js
new file mode 100644
index 0000000..af4a060
--- /dev/null
+++ b/src/Store.js
@@ -0,0 +1,40 @@
+module.exports = function Store(_store){
+ var self = this;
+
+ var store = [];
+
+ if( isArray(_store) ){
+ addArray(_store);
+ }
+
+ function isObject(obj){ return !!obj && Object.prototype.toString.call(obj) == '[object Object]'; }
+ function isArray(obj){ return !!obj && Object.prototype.toString.call(obj) == '[object Array]'; }
+
+ function addObject(data){
+ store.push(data);
+ return data;
+ }
+
+ function addArray(data){
+ var added = [];
+ for (var i = 0; i < data.length; i++)
+ if( isObject(data[i]) )
+ added.push(addObject(data[i]));
+ return added;
+ }
+
+ self.clear = function(){
+ store.length = 0;
+ return store;
+ };
+
+ self.get = function(){
+ return store;
+ };
+
+ self.put = function(data){
+ if( isObject(data) ) return addObject(data);
+ if( isArray(data) ) return addArray(data);
+ return undefined;
+ };
+};
\ No newline at end of file
diff --git a/src/Templater.js b/src/Templater.js
new file mode 100644
index 0000000..c3a6580
--- /dev/null
+++ b/src/Templater.js
@@ -0,0 +1,15 @@
+module.exports = function Templater(){
+ var self = this;
+
+ var templatePattern = /\{(.*?)\}/g;
+
+ self.setTemplatePattern = function(newTemplatePattern){
+ templatePattern = newTemplatePattern;
+ };
+
+ self.render = function(t, data){
+ return t.replace(templatePattern, function(match, prop) {
+ return data[prop] || match;
+ });
+ };
+};
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..881a944
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,113 @@
+;(function(window,document,undefined){
+ 'use strict';
+
+ var Searcher = require('./Searcher');
+ var Templater = require('./Templater');
+ var Store = require('./Store');
+ var JSONLoader = require('./JSONLoader');
+
+ var searcher = new Searcher();
+ var templater = new Templater();
+ var store = new Store();
+ var jsonLoader = new JSONLoader();
+
+
+ window.SimpleJekyllSearch = new SimpleJekyllSearch();
+ function SimpleJekyllSearch(){
+ var self = this;
+
+ var requiredOptions = [
+ 'searchInput',
+ 'resultsContainer',
+ 'dataSource',
+ ];
+ var opt = {
+ searchInput: null,
+ resultsContainer: null,
+ dataSource: [],
+ searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
+ noResultsText: 'No results found',
+ limit: 10,
+ fuzzy: false,
+ };
+
+ function initWithJSON(json){
+ store.put(opt.dataSource);
+ registerInput();
+ }
+
+ function initWithURL(url){
+ jsonLoader.load(url, function gotJSON(err,json){
+ if( !err ) {
+ store.put(json);
+ registerInput();
+ }else{
+ throwError('failed to get JSON (' + url + ')');
+ }
+ });
+ }
+
+ self.init = function(_opt){
+ validateOptions(_opt);
+ assignOptions(_opt);
+ if( isJSON(opt.dataSource) ){
+ initWithJSON(opt.dataSource);
+ }else{
+ initWithURL(opt.dataSource);
+ }
+ };
+
+
+ function throwError(message){ throw new Error('SimpleJekyllSearch --- '+ message); }
+
+ function validateOptions(_opt){
+ for(var i = 0; i < requiredOptions.length; i++){
+ var req = requiredOptions[i];
+ if( !_opt[req] )
+ throwError('You must specify a ' + req);
+ }
+ }
+
+ function assignOptions(_opt){
+ for(var option in opt)
+ opt[option] = _opt[option] || opt[option];
+ }
+
+ function isJSON(json){
+ try{
+ return json instanceof Object && JSON.parse(JSON.stringify(json));
+ }catch(e){
+ return false;
+ }
+ }
+
+ function emptyResultsContainer(){
+ opt.resultsContainer.innerHTML = '';
+ }
+
+ function appendToResultsContainer(text){
+ opt.resultsContainer.innerHTML += text;
+ }
+
+ function registerInput(){
+ opt.searchInput.addEventListener('keyup', function(e){
+ if( e.target.value.length == 0 ){
+ emptyResultsContainer();
+ return;
+ }
+ render( searcher.search(store, e.target.value) );
+ });
+ }
+
+ function render(results){
+ emptyResultsContainer();
+ if( results.length == 0 ){
+ return appendToResultsContainer(opt.noResultsText);
+ }
+ for (var i = 0; i < results.length; i++) {
+ appendToResultsContainer( templater.render(opt.searchResultTemplate, results[i]) );
+ };
+ }
+
+ };
+})(window,document);