./._picky.extensions.js000644 000765 000024 00000000273 11456336714 015552 0ustar00adminstaff000000 000000 Mac OS X  2‰»ATTR7`˜»˜#˜#com.macromates.caret{ column = 19; line = 11; }picky.extensions.js000644 000765 000024 00000000466 11456336714 015204 0ustar00adminstaff000000 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.js000644 000765 000024 00000002147 11456336714 015524 0ustar00adminstaff000000 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.js000644 000765 000024 00000003476 11456336714 013722 0ustar00adminstaff000000 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.js000644 000765 000024 00000012255 11456336714 013756 0ustar00adminstaff000000 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.js000644 000765 000024 00000000270 11456336714 014737 0ustar00adminstaff000000 000000 Mac OS X  2†¸ATTR7`”¸˜ ˜ com.macromates.caretxœ«æR‚äüœÒÜ<[k0?'3/«‹™picky.backend.js000644 000765 000024 00000005153 11456336714 014372 0ustar00adminstaff000000 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.js000644 000765 000024 00000006336 11456336714 015172 0ustar00adminstaff000000 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.js000644 000765 000024 00000000273 11456654311 014625 0ustar00adminstaff000000 000000 Mac OS X  2‰»ATTR7`•»˜#˜#com.macromates.caret{ column = 18; line = 31; }picky.client.js000644 000765 000024 00000003603 11456654311 014253 0ustar00adminstaff000000 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.js000644 000765 000024 00000002162 11456336714 015112 0ustar00adminstaff000000 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 = $("
" + t('results.addination.more') + "
"); result.bind('click', { offset: range.offset }, view.addinationClicked); return result; } else { return ''; } }; this.render = render; };picky.results_renderer.js000644 000765 000024 00000007653 11456336714 016401 0ustar00adminstaff000000 000000 var PickyResultsRenderer = function(addination) { // Adds asterisks to the last token. // var no_asterisks = ['street_number', 'zipcode']; // TODO Works. Parametrize! var asteriskifyLastToken = function(combination) { var last_part = combination[combination.length-1]; var parts = combination.slice(0, combination.length-1); if (parts == []) { parts = [parts]; } if (!no_asterisks.include(last_part[0])) { // Replace with * unless there is already one or a tilde. // if (last_part[1].match(/[^\*~]$/)) { last_part[1] += '*'; } } parts.push(last_part); return parts; }; // Replaces the category with an explanation of the category. // var explainCategory = function(combination) { var explanations = Localization.explanations && Localization.explanations[PickyI18n.locale] || {}; // TODO var parts = []; var combo; for (var i = 0, l = combination.length; i < l; i++) { combo = combination[i]; var explanation = combo[0]; explanation = explanations[explanation] || explanation; parts.push([explanation, combo[1]]); } return parts; }; // // var explain = function(type, combination) { var explanation_delimiter = Localization.explanation_delimiters[PickyI18n.locale]; var parts = explainCategory(asteriskifyLastToken(combination)); var replaced = $.map(parts, function(part) { var category = part[0].replace(/([\w\sÄäÖöÜüéèà]+)/, "$1"); var token = part[1]; return [category, token].join(' '); }); replaced = replaced.join(' ' + explanation_delimiter + ' '); return '
' + type + ' ' + replaced + '
'; }; // TODO Make customizable. // var renderHeader = function(data, allocation) { // TODO Make type definable. (Mapping, i18n) // var header_html = '
'; header_html += explain(allocation.type, allocation.combination); if (data.offset > 0) { header_html += '
'; // searchEngine.focus(); } // TODO Parametrize! // var names = ''; // var firstEntryName = $(allocation.entries[0]).find('.name').html(); // var lastEntryName = $(allocation.entries[allocation.entries.length-1]).find('.name').html(); // if(firstEntryName == lastEntryName) { // var firstEntryFirstName = $(allocation.entries[0]).find('.first_name').html(); // var lastEntryFirstName = $(allocation.entries[allocation.entries.length-1]).find('.first_name').html(); // names = '
' + firstEntryName + ', ' + firstEntryFirstName + ' ' + t('common.to') + ' ' + lastEntryFirstName + '
'; // } // else { // names = '
' + firstEntryName + ' ' + t('common.to') + ' ' + lastEntryName + '
'; // } // var rangeStart = data.offset + 1; // var rangeEnd = data.offset + allocation.entries.length; // var rangeText = (rangeStart == rangeEnd) ? rangeStart : rangeStart + '-' + rangeEnd; // var range = '
' + rangeText + ' ' + t('common.of') + ' ' + data.total + '
'; // if (data.total > 20) { // TODO Make settable. // // header_html += '
'; // TODO // // header_html += names; // TODO // // header_html += range; // TODO // } // For smooth addination scrolling. Don't ask. // header_html += '
'; return header_html; }; // Render results with the data. // this.render = function(data) { var results = $('#picky .results'); // TODO Extract, also from view. data.allocations.each(function(i, allocation) { results.append(renderHeader(data, allocation)) // TODO header.render(data); .append(allocation.entries.join('')) .append(addination.render(data)); }); }; };./._picky.allocation_renderer.js000644 000765 000024 00000000274 11456654060 017364 0ustar00adminstaff000000 000000 Mac OS X  2Š¼ATTR7`’¼˜$˜$com.macromates.caret{ column = 37; line = 285; }picky.allocation_renderer.js000644 000765 000024 00000021243 11456654060 017011 0ustar00adminstaff000000 000000 function AllocationRenderer(allocationChosenCallback) { var self = this; var locale = PickyI18n.locale; var qualifiers = Localization.qualifiers && Localization.qualifiers[locale] || {}; var explanations = Localization.explanations && Localization.explanations[locale] || {}; var location_delimiter = Localization.location_delimiters[locale]; var explanation_delimiter = Localization.explanation_delimiters[locale]; // Those are of interest to the public. // this.text = ''; this.query = ''; this.explanation = ''; // TODO parametrize. // var no_ellipses = ['street_number', 'zipcode']; // Contracts the originals of the zipped. // function contract(zipped) { var hash = {}; // remembers the values var insert = {}; // remembers the insertion locations var remove = []; // remembers the remove indexes var i; for (i = 0, l = zipped.length; i < l; i++) { var key = zipped[i][0]; if (key in hash) { hash[key] = hash[key] + ' ' + zipped[i][1]; remove.push(i); } else { hash[key] = zipped[i][1]; insert[i] = key; } } // Insert the ones from the hash. for (i in insert) { zipped[i][1] = hash[insert[i]]; } // Remove the ones from zipped we don't like. From the end. for (i = remove.length-1; i >= 0; i--) { zipped.remove(remove[i]); } return zipped; }; this.contract = contract; // TODO Parametrize! var specialWhoCases = { // Use the actual methods, not strings. "maiden_name" : { format:"(-%1$s)", method:'capitalize', ignoreSingle:true }, "name" : { format:"%1$s", method:'toUpperCase', ignoreSingle:true }, "first_name" : { format:"%1$s", method:"capitalize" } }; function handleWho(both, singleParam) { var single = singleParam || false; var allocation = both[0]; var word = both[1]; var formatting = specialWhoCases[allocation]; if (formatting) { if (formatting.method) { word = word[formatting.method](); } if (formatting.format) { word = formatting.format.replace(/%1\$s/, word); } } var explanation = explanations[allocation] || allocation; if (single && !(formatting && formatting.ignoreSingle)) { return word + ' (' + explanation + ')'; } return word; } // Handles the first (who) part. // // Rules: // * If there is no thing, do return an empty string. // * If there is only one thing, add an explanation. // * If there are multiple things, handle special cases. // Without special cases, the name is always in front. // The things are separated by commas, and explained. // If there are multiple instances of the same category, they are contracted. // // Note:   to not disconnect the explanation from the query text. // function who(zipped) { if (zipped.length == 0) { return ''; } else if (zipped.length == 1) { return handleWho(zipped[0], true); } else { // else, sort, special cases etc. var result = []; var append = []; zipped = contract(zipped); for (var i = 0, l = zipped.length; i < l; i++) { if (zipped[i][0] == 'first_name') { result.unshift(handleWho(zipped[i])); // prepend the first name } else { if (zipped[i][0] == 'maiden_name') { append.push(handleWho(zipped[i])); } else { result.push(handleWho(zipped[i])); } } }; if (append.length > 0) { result.push(append); }; return result.join(' '); } } this.who = who; function replacerFor(zipped) { return function(_, category) { for (var i = 0, l = zipped.length; i < l; i++) { if (zipped[i][0] == category) { return zipped[i][1]; }; }; return ''; }; }; // Handles the second (where) part. // // Rules: // * If there is no location, do return an empty string. // * If there is only a zipcode, add a []. // * If there is only a city, add nothing. // * If there are both, zipcode needs to be first. // TODO Contraction of multiple "cities" and/or zipcode. // var locations = { 'zipcode':(':zipcode [' + explanations.city + ']'), 'city':':city', 'city,zipcode':':zipcode :city' }; function where(zipped) { if (zipped.length == 0) { return ''; }; zipped = contract(zipped); var key_ary = zipped; key_ary.sort(function(zipped1, zipped2) { return zipped1[0] < zipped2[0] ? -1 : 1; }); // Now that it's sorted, get the right string. var key = []; for (var i = 0, l = key_ary.length; i < l; i++) { key.push(key_ary[i][0]); }; var loc = locations[key]; // Replace inside string. var result = loc.replace(/:(zipcode|city)/g, replacerFor(zipped)); return result; }; this.where = where; function handleSingleWhat(both) { var allocation = both[0]; var word = both[1]; var explanation = explanations[allocation] || allocation; return word + ' (' + explanation + ')'; } function what(zipped) { if (zipped.length == 0) { return ''; }; result = []; zipped = contract(zipped); for (var i = 0, l = zipped.length; i < l; i++) { result.push(handleSingleWhat(zipped[i])); } return result.join(', '); }; this.what = what; // Orders the allocation identifiers according to // [, , ] // Returns a reordered array. // // TODO Rename "group". // var who_qualifiers = ['first_name', 'name', 'maiden_name']; var where_qualifiers = ['zipcode', 'city']; function trisect(zipped) { var who_parts = []; var what_parts = []; var where_parts = []; for (var i = 0, l = zipped.length; i < l; i++) { var combination = zipped[i]; if (where_qualifiers.include(combination[0])) { where_parts.push(combination); } else if (who_qualifiers.include(combination[0])) { who_parts.push(combination); } else { what_parts.push(combination); } } // Ellipsisize the last part var alloc_part; if (where_parts.length > 0) { alloc_part = where_parts[where_parts.length-1]; } else if (what_parts.length > 0) { alloc_part = what_parts[what_parts.length-1]; } else if (who_parts.length > 0) { alloc_part = who_parts[who_parts.length-1]; } // always results in a part if (!no_ellipses.include(alloc_part[0])) { alloc_part[1] += '...'; } // TODO * var rendered_who = who(who_parts); var rendered_what = what(what_parts); var rendered_where = where(where_parts); return [rendered_who, rendered_what, rendered_where]; }; this.trisect = trisect; // Fuses a possible who part to a possible what part to a possible where part. // // e.g. , in // // Note:   to not disconnect the location delimiter (e.g. "in") from the location. // // TODO Parametrize! // var who_what_join = ', '; var whowhat_where_join = ' ' + location_delimiter + ' '; function fuse(parts) { var who = parts[0], what = parts[1], where = parts[2]; var who_what = ''; if (who != '') { if (what != '') { who_what = [who, what].join(who_what_join); } else { who_what = who; } } else { who_what = what; } if (where == '') { return who_what; }; return [who_what, where].join(whowhat_where_join); }; this.fuse = fuse; // Creates a query string from combination and originals. // function querify(zipped) { var query_parts = []; var qualifier; for (var i in zipped) { qualifier = zipped[i][0]; qualifier = qualifiers[qualifier] || qualifier; // Use the returned qualifier if none is given. query_parts[i] = qualifier + ':' + zipped[i][1]; }; return query_parts.join(' '); }; this.querify = querify; // // function suggestify(zipped) { return fuse(trisect(zipped)); }; // Generates the text and the link. // var generate = function() { this.query = querify(combination); this.text = suggestify(combination); return self; }; // // var listItem = function(text, count) { return $('
  • ' + text + '
    ' + count + '
  • '); }; var render = function(allocation) { var combination = allocation.combination; var type = allocation.type; var count = allocation.count; var query = querify(combination); var item = listItem(suggestify(combination), count); // TODO Move this outwards? // item.bind('click', { query: query }, allocationChosenCallback); return item; }; this.render = render; };./._picky.allocations_cloud.js000644 000765 000024 00000000272 11456644337 017053 0ustar00adminstaff000000 000000 Mac OS X  2ˆºATTR7`“º˜"˜"com.macromates.caret{ column = 4; line = 26; }picky.allocations_cloud.js000644 000765 000024 00000004204 11456644337 016500 0ustar00adminstaff000000 000000 var PickyAllocationsCloud = function(view) { var allocations = $('#picky .allocations'); var shownAllocations = allocations.find('.shown'); var showMoreAllocations = allocations.find('.more'); var hiddenAllocations = allocations.find('.hidden'); // Show the cloud. // var show = function(data) { render(data.allocations); allocations.show(); }; // Hide the cloud. // var hide = function() { allocations.hide(); }; var clearAllocationCloud = function() { shownAllocations.empty(); showMoreAllocations.hide(); hiddenAllocations.empty().hide(); }; // // var allocationChosen = function(event) { hide(); view.allocationChosen(event); }; var allocationRenderer = new AllocationRenderer(allocationChosen); var createAllocationList = function(allocations) { var shown = []; allocations.each(function(i, allocation) { shown.push(allocationRenderer.render(allocation)); // // TODO Combine. // allocationRenderer.generate(); // var listItem = renderListItem(allocationRenderer); // shown.push(listItem); }); return shown; }; var renderList = function(list) { if (list.length == 0) { return $('#search .allocations').hide(); } var maxSuggestions = 3; clearAllocationCloud(); if (list.length > maxSuggestions) { $.each(list.slice(0,maxSuggestions-1), function(i, item) { shownAllocations.append(item); }); $.each(list.slice(maxSuggestions-1), function(i, item) { hiddenAllocations.append(item); }); showMoreAllocations.show(); } else { $.each(list, function(i, item) { shownAllocations.append(item); }); } return $('#search .allocations').show(); }; // Render the allocation list. // var render = function(allocations) { renderList(createAllocationList(allocations)); }; // Install handlers. // showMoreAllocations.click(function() { showMoreAllocations.hide(); hiddenAllocations.show(); }); // Expose hide and show. // this.hide = hide; this.show = show; };