./._picky.extensions.js 000644 000765 000024 00000000273 11456336714 015552 0 ustar 00admin staff 000000 000000 Mac OS X 2 ‰ » ATTR 7`˜ » ˜ # ˜ # com.macromates.caret { column = 19; line = 11; } picky.extensions.js 000644 000765 000024 00000000466 11456336714 015204 0 ustar 00admin staff 000000 000000 Array.prototype.index = function(val) { for(var i = 0, l = this.length; i < l; i++) { if(this[i] == val) return i; } return null; }; Array.prototype.include = function(val) { return this.index(val) !== null; }; Array.prototype.remove = function(index) { this.splice(index, 1); return this; }; picky.translations.js 000644 000765 000024 00000002147 11456336714 015524 0 ustar 00admin staff 000000 000000 // Translations // var PickyI18n = { }; // Set the correct locale for all js code. // $(function() { PickyI18n.locale = $('html').attr('lang') || 'en'; }); var dictionary = { common:{ join: {de:'und',fr:'et',it:'e',en:'and',ch:'und'}, 'with': {de:'mit',fr:'avec',it:'con',en:'with',ch:'mit'}, of: {de:'von',fr:'de',it:'di',en:'of',ch:'vo'}, to: {de:'bis',fr:'à ',it:'alla',en:'to',ch:'bis'} }, results:{ addination:{ more:{ de: 'Weitere Resultate', fr: 'Autres résultats', it: 'Altri risultati', en: 'More results', ch: 'Mee Resultaat' } }, header:{ de: 'Ergebnisse', fr: 'Résultats', it: 'Risultati', en: 'Results', ch: 'Ergäbnis' } } }; var t = function(key) { var locale = PickyI18n.locale || 'en'; var keys = key.split('.').concat(locale); var current = dictionary; for (var i = 0, l = keys.length; i < l; i++) { current = current[keys[i]]; if (current == undefined) { current = 'Translation missing: ' + key + '.' + locale; break; } }; return current; }; picky.data.js 000644 000765 000024 00000003476 11456336714 013722 0 ustar 00admin staff 000000 000000 // The data is basically the model behind the search. // // Container for an allocation. // function Allocation(type, weight, count, combination, ids, rendered) { var self = this; this.type = type; // 'books' this.weight = weight; // 5.14 this.count = count; // 14 this.combination = combination; // [['title', 'Old', 'old'], ['title', 'Man', 'man']] this.ids = ids || []; this.rendered = rendered || []; this.entries = this.rendered; this.isType = function(name) { return name == self.type; }; }; // Container for the allocations. // // allocs (should) come preordered by weight. // function Allocations(allocations) { var self = this; this.allocations = []; // Wrap and save the allocations. // for (var i = 0, l = allocations.length; i < l; i++) { var alloc = allocations[i]; var new_allocation = new Allocation(alloc[0], alloc[1], alloc[2], alloc[3], alloc[4], alloc[5]); this.allocations.push(new_allocation); } this.length = this.allocations.length; this.each = function(callback) { return $.each(this.allocations, callback); }; }; // Container for the types. // // data: // offset: X // duration: X // total: X // allocations: // Allocation[] of [weight, count, combination, Entry[] of [id, content]] // function PickyData(data) { var self = this; // Attributes. // var total = data.total; var duration = data.duration; var offset = data.offset; var allocations = new Allocations(data.allocations || []); // Expose some attributes. // this.total = total; this.duration = duration; this.offset = offset; this.allocations = allocations; // Are there any results? // var isEmpty = function() { return total == 0; }; this.isEmpty = isEmpty; }; picky.view.js 000644 000765 000024 00000012255 11456336714 013756 0 ustar 00admin staff 000000 000000 // Add PickyResults? var PickyView = function(picky_controller, config) { var controller = picky_controller; var showResultsLimit = config.showResultsLimit || 10; var searchField = $('#picky input.query'); var clearButton = $('#picky div.reset'); var searchButton = $('#picky input.search_button'); var resultCounter = $('#picky div.status'); var dashboard = $('#picky .dashboard'); var results = $('#picky .results'); // Push into results. var noResults = $('#picky .no_results'); var addination = new PickyAddination(this, results); // Push into results. var allocationsCloud = new PickyAllocationsCloud(this); var resultsRenderer = new PickyResultsRenderer(addination); // TODO Rename results. // Toggle the clear button visibility. // var showClearButton = function() { clearButton.fadeTo(166, 1.0); }; var hideClearButton = function() { clearButton.fadeTo(166, 0.0); }; // TODO Move to results var clearResults = function() { results.empty(); }; var hideEmptyResults = function() { noResults.hide(); }; var focus = function() { searchField.focus(); }; var select = function() { searchField.select(); }; // Cleans the interface of any results or choices presented. // var clean = function() { allocationsCloud.hide(); clearResults(); hideEmptyResults(); }; // Resets the whole view to the inital state. // var reset = function(to_text) { searchField.val(to_text); hideClearButton(); setSearchStatus('empty'); resultCounter.empty(); clean(); }; var bindEventHandlers = function() { searchField.keyup(function(event) { if (isTextEmpty()) { reset(); controller.searchTextCleared(); } else { controller.searchTextEntered(text(), event); showClearButton(); } }); searchButton.click(function(event) { if (!isTextEmpty()) { controller.searchButtonClicked(text()); } }); clearButton.click(function() { reset(''); controller.clearButtonClicked(); focus(); }); }; var text = function() { return searchField.val(); }; this.text = text; // TODO Remove. var isTextEmpty = function() { return text() == ''; }; var showEmptyResults = function() { clean(); updateResultCounter(0); noResults.show(); showClearButton(); }; var showTooManyResults = function(data) { clean(); showClearButton(); allocationsCloud.show(data); updateResultCounter(data.total); }; var showResults = function(data) { clean(); updateResultCounter(data.total); resultsRenderer.render(data); results.show(); showClearButton(); }; var appendResults = function(data) { addination.remove(); // TODO Where should this be? resultsRenderer.render(data); $.scrollTo('#picky .results div.info:last', { duration: 500, offset: -12 }); }; var updateResultCounter = function(total) { resultCounter.text(total); // ((total > 999) ? '999+' : total); // TODO Decide on this. flashResultCounter(total); }; var alertThreshold = 5; var flashResultCounter = function(total) { if (total > 0 && total <= alertThreshold) { resultCounter.fadeTo('fast', 0.5).fadeTo('fast', 1); } }; var tooManyResults = function(data) { return data.total > showResultsLimit && data.allocations.length > 1; }; var resultStatusFor = function(data) { if (data.isEmpty()) { return 'none'; }; if (tooManyResults(data)) { return 'support'; } return 'ok'; }; var setSearchStatus = function(statusClass) { dashboard.attr('class', 'dashboard ' + statusClass); }; var setSearchStatusFor = function(data) { setSearchStatus(resultStatusFor(data)); }; // Insert a search text into the search field. // Field is always selected when doing that. // var insert = function(text) { searchField.val(text); select(); }; this.insert = insert; // Callbacks. // // Full results handling. // var fullResultsCallback = function(data) { setSearchStatusFor(data); if (data.isEmpty()) { showEmptyResults(); } else if (tooManyResults(data)) { showTooManyResults(data); } else { if (data.offset == 0) { showResults(data); } else { appendResults(data); } }; focus(); }; this.fullResultsCallback = fullResultsCallback; // Live results handling. // var liveResultsCallback = function(data) { setSearchStatusFor(data); updateResultCounter(data.total); }; this.liveResultsCallback = liveResultsCallback; // Callback for when an allocation has been chosen // in the allocation cloud. // var allocationChosen = function(event) { var text = event.data.query; searchField.val(text); controller.allocationChosen(text); }; this.allocationChosen = allocationChosen; // Callback for when the addination has been clicked. // var addinationClicked = function(event) { controller.addinationClicked(text(), event); }; this.addinationClicked = addinationClicked; // // bindEventHandlers(); focus(); }; ./._picky.backend.js 000644 000765 000024 00000000270 11456336714 014737 0 ustar 00admin staff 000000 000000 Mac OS X 2 † ¸ ATTR 7`” ¸ ˜ ˜ com.macromates.caret xœ«æR ‚äüœÒÜ<[k0?'3/« ‹™ picky.backend.js 000644 000765 000024 00000005153 11456336714 014372 0 ustar 00admin staff 000000 000000 // Core search backend. // var PickyBackend = function(url) { // Get returns the data without handling timestamps and whatnot. // var get = function(query, controllerCallback, offset, specificParams) { var params = specificParams || {}; params = $.extend({ query: query, offset: offset }, specificParams); // Wrap any data returned in a PickyData object. // var callback = function(data_hash) { if (controllerCallback) { controllerCallback(new PickyData(data_hash)); } }; $.ajax({ type: 'GET', url: url, data: params, success: callback, dataType: 'json'}); }; var search = function(query, controllerCallback, offset, specificParams, specificTimestamps) { // Wrap the given callback. // var callback = function(data) { if (controllerCallback) { controllerCallback(specificTimestamps, data); } }; get(query, callback, offset, specificParams); }; this.search = search; }; // Live search backend. // var LiveBackend = function(url, callback) { var backend = new PickyBackend(url); var search = function(query, controllerCallback, offset, specificParams, fullTimestamps) { var specificTimestamps = fullTimestamps || {}; latestRequestTimestamp = new Date(); specificTimestamps.live = latestRequestTimestamp; // Wrap the given callback. // // Note: Binds the latest request timestamp for later comparison. // var callback = function(timestamps, data) { if (!timestamps.live || timestamps.live == latestRequestTimestamp) { if (controllerCallback) { controllerCallback(data); } }; }; // Pass in the timestamp for later comparison. // backend.search(query, callback, offset, specificParams, specificTimestamps); }; this.search = search; }; // Full search backend. // var FullBackend = function(url) { var backend = new PickyBackend(url); var search = function(query, controllerCallback, offset, specificParams, givenTimestamps) { var specificTimestamps = givenTimestamps || {}; latestRequestTimestamp = new Date(); specificTimestamps.full = latestRequestTimestamp; // Wrap the given callback. // // Note: Binds the latest request timestamp for later comparison. // var callback = function(timestamps, data) { if (!timestamps.full || timestamps.full == latestRequestTimestamp) { if (controllerCallback) { controllerCallback(data); } }; }; // Pass in the timestamp for later comparison. // backend.search(query, callback, offset, specificParams, specificTimestamps); }; this.search = search; }; picky.controller.js 000644 000765 000024 00000006336 11456336714 015172 0 ustar 00admin staff 000000 000000 var PickyController = function(config) { var view = new PickyView(this, config); var backends = config.backends; var beforeCallback = config.before || function(params, query, offset) { }; var successCallback = config.success || function(data, query) { }; var afterCallback = config.after || function(data, query) { }; // If the given backend cannot be found, ignore the search request. // var search = function(type, query, callback, offset, specificParams) { var currentBackend = backends[type]; if (currentBackend) { currentBackend.search(query, callback, offset, specificParams); }; }; var fullSearchCallback = function(data, query) { data = successCallback(data, query) || data; view.fullResultsCallback(data); afterCallback(data, query); }; var fullSearch = function(query, possibleOffset, possibleParams) { var params = possibleParams || {}; var offset = possibleOffset || 0; liveSearchTimer.stop(); params = beforeCallback(params, query, offset) || params; search('full', query, fullSearchCallback, offset, params); }; var liveSearchCallback = function(data, query) { data = successCallback(data, query) || data; view.liveResultsCallback(data); afterCallback(data, query); }; var liveSearch = function(query, possibleParams) { var params = possibleParams || {}; params = beforeCallback(params) || params; search('live', query, liveSearchCallback, 0); }; // The timer is initially instantly stopped. // var liveSearchTimer = $.timer(180, function(timer) { liveSearch(view.text()); timer.stop(); }); liveSearchTimer.stop(); this.insert = function(query, full) { view.insert(query); if (full) { fullSearch(query); } // TODO }; var clearButtonClicked = function() { liveSearchTimer.stop(); }; this.clearButtonClicked = clearButtonClicked; var searchTextCleared = function() { liveSearchTimer.stop(); }; this.searchTextCleared = searchTextCleared; var shouldTriggerSearch = function(event) { var validTriggerKeys = [ 0, // special char (ä ö ü etc...) 8, // backspace 13, // enter 32, // space 46, // delete 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, // numbers 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 // a-z ]; return $.inArray(event.keyCode, validTriggerKeys) > -1; }; var searchTextEntered = function(text, event) { if (shouldTriggerSearch(event)) { if (event.keyCode == 13) { fullSearch(text); } else { liveSearchTimer.reset(); } } }; this.searchTextEntered = searchTextEntered; var searchButtonClicked = function(text) { fullSearch(text); }; this.searchButtonClicked = searchButtonClicked; var allocationChosen = function(text) { fullSearch(text); }; this.allocationChosen = allocationChosen; // Move to a view object. var addinationClicked = function(text, event) { fullSearch(text, event.data.offset); }; this.addinationClicked = addinationClicked; }; ./._picky.client.js 000644 000765 000024 00000000273 11456654311 014625 0 ustar 00admin staff 000000 000000 Mac OS X 2 ‰ » ATTR 7`• » ˜ # ˜ # com.macromates.caret { column = 18; line = 31; } picky.client.js 000644 000765 000024 00000003603 11456654311 014253 0 ustar 00admin staff 000000 000000 var Localization = { // TODO Remove. location_delimiters: { de:'in', fr:'à ', it:'a', en:'in', ch:'in' }, explanation_delimiters: { de:'und', fr:'et', it:'e', en:'and', ch:'und' } }; // The client handles parameters and // offers an insert method. // var PickyClient = function(config) { // Params handling. // // This is used to generate the correct query strings, localized. // // e.g with locale it: // ['title', 'ulysses', 'Ulysses'] => 'titolo:ulysses' // // This needs to correspond to the parsing in the search engine. // Localization.qualifiers = config.qualifiers || {}; // This is used to explain the preceding word in the suggestion text. // // e.g. with locale it: // ['title', 'ulysses', 'Ulysses'] => 'Ulysses (titolo)' // Localization.explanations = config.explanations || {}; // TODO Explain. // Localization.explanation_delimiters = { de:'und', fr:'et', it:'e', en:'and', ch:'und' }; // Either you pass it a backends hash with full and live, // or you pass it full and live (urls), which will then // be wrapped in appropriate backends. // var backends = config.backends; if (backends) { backends.live || alert('Both a full and live backend must be provided.'); backends.full || alert('Both a full and live backend must be provided.'); } else { config.backends = { live: config.live && new LiveBackend(config.live) || alert('A live backend path must be provided.'), full: config.full && new FullBackend(config.full) || alert('A live backend path must be provided.') }; } // The central Picky controller. // var controller = config.controller && new config.controller(config) || new PickyController(config); // Insert a query into the client and run it. // Default is a full query. // this.insert = function(query, full) { controller.insert(query, full || true); }; }; picky.addination.js 000644 000765 000024 00000002162 11456336714 015112 0 ustar 00admin staff 000000 000000 var PickyAddination = function(view, results) { // Calculate the addination range. // var calculateRange = function(data, correction) { var correction = correction || 0; var numberOfResults = 20; // Make parametrizable. var offset = data.offset + numberOfResults + correction; var end = offset + numberOfResults; var total = data.total; if (total < end) { end = total; } return { offset:offset, start:(offset+1), end:end }; }; // Remove the addination. // var remove = function() { results.find('.addination').remove(); }; this.remove = remove; // Renders the addination; // var render = function(data) { var total = data.total; var range = calculateRange(data); if (range.offset < total) { var result = $("