├── .gitignore ├── 2011_02_08 ├── PARTICIPANTES ├── RETROSPECTIVA ├── ocr.py └── test_ocr.py ├── 2011_02_15 ├── PARTICIPANTES ├── RETROSPECTIVA ├── test_wordwrap.py └── wordwrap.py ├── 2011_02_22 ├── PARTICIPANTES ├── README ├── RETROSPECTIVA ├── demo │ ├── bg.gif │ ├── index.html │ ├── jquery.autocomplete.css │ ├── jquery.autocomplete.js │ ├── jquery.js │ ├── localdata.js │ └── main.css └── tests │ ├── SpecRunner.html │ ├── fixtures │ └── localdata.js │ ├── lib │ ├── jasmine-1.0.0 │ │ ├── MIT.LICENSE │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ └── jasmine.js │ └── jquery-1.4.4 │ │ └── jquery.js │ ├── spec │ └── PluginSpec.js │ ├── src │ └── Plugin.js │ └── stylesheets │ ├── jquery.autocomplete.css │ └── main.css ├── 2011_03_01 ├── PARTICIPANTES ├── README.markdown ├── RETROSPECTIVA ├── primas.py └── test_primas.py ├── 2011_03_04 ├── README.rst ├── count_and_say.py ├── fizz_buzz.py ├── participantes.rst ├── retrospectiva.rst ├── test_count_and_say.py └── test_fizzbuzz.py ├── 2011_03_10 ├── PARTICIPANTES ├── README ├── RETROSPECTIVA ├── codigo.py └── test.py ├── 2011_03_15 ├── .autotest ├── .rspec ├── Gemfile ├── Gemfile.lock ├── PARTICIPANTES ├── README ├── RETROSPECTIVA ├── Rakefile ├── bowling.rb └── spec │ └── bowling_spec.rb ├── 2011_03_16 ├── PARTICIPANTES ├── README ├── RETROSPECTIVA ├── caixa.py └── test_caixa.py ├── 2011_03_23 ├── README.rst ├── anobissexto.py ├── encotel.py ├── participantes.rst ├── retrospectiva.rst ├── test_anobissexto.py └── test_encotel.py ├── 2011_04_13 ├── codigo.py ├── readme.rst ├── retrospectiva.rst └── test.py ├── 2011_04_20 ├── codigo.py ├── count_numbers.py ├── participantes.rst ├── retrospectiva.rst ├── test.py └── test_count_numbers.py ├── 2011_07_12 ├── expressao_matematica.py ├── participantes.rst ├── retrospectiva.rst └── test_expressao_matematica.py ├── 2011_07_19 ├── Makefile ├── problem.c └── retrospectiva.txt ├── 2011_07_26 ├── Makefile ├── retrospectiva.rst └── test.cpp ├── 2011_08_09 ├── README.markdown ├── primas.py ├── problemas.markdown ├── retrospectiva.markdown └── test_primas.py ├── 2011_08_16 ├── .rvmrc ├── RETROSPECTIVA.md ├── comentarios.rb └── spec │ └── comentarios_spec.rb ├── 2011_08_23 ├── amigos.py ├── retro.rst └── tests.py ├── 2011_10_19 ├── codigo.py ├── retrospectiva.txt └── test.py ├── 2012_03_06 ├── booleanos.py ├── problemas ├── retrospectiva └── test_booleanos.py ├── 2012_03_14 ├── .DS_Store ├── encontrar_telefone.py ├── retrospectiva.txt └── test_encontrar_telefone.py ├── 2012_04_03 ├── ano_bissexto │ ├── ano_bissexto.py │ └── test.py ├── caixa_eletronico │ ├── caixa_eletronico.py │ └── test.py └── retrospectiva └── README /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | -------------------------------------------------------------------------------- /2011_02_08/PARTICIPANTES: -------------------------------------------------------------------------------- 1 | Hugo Tavares 2 | Siminino 3 | Zacarias 4 | Fiuk 5 | Andrews 6 | Hugo Antunes 7 | Tarsis Azevedo 8 | Quixada 9 | Igor 10 | Vinicius 11 | Thatiana 12 | Tina 13 | -------------------------------------------------------------------------------- /2011_02_08/RETROSPECTIVA: -------------------------------------------------------------------------------- 1 | :-) 2 | 3 | * bastante gente 4 | * nível de exercício legal 5 | * estrutura excelente 6 | 7 | 8 | ------------------------- 9 | 10 | :`( 11 | 12 | * horário ruim 13 | * teclado usb 14 | * faltou explicar melhor o que estava fazendo 15 | * muitas distrações 16 | 17 | 18 | -------------------------- 19 | 20 | melhorar: 21 | 22 | * menos falação 23 | * começar por volta de 19:30 24 | 25 | 26 | 27 | == Próximo organizador: Tarsis 28 | -------------------------------------------------------------------------------- /2011_02_08/ocr.py: -------------------------------------------------------------------------------- 1 | def ocr(numero): 2 | numeros = { 3 | '\n \n |\n |\n': 1, 4 | '\n __\n __|\n|__\n': 2, 5 | '\n __\n __|\n __|\n': 3, 6 | '\n\n|__|\n |\n': 4, 7 | '\n __\n|__\n __|\n': 5, 8 | '\n __\n|__ \n|__|\n': 6, 9 | '\n __\n |\n |\n': 7, 10 | '\n __\n|__|\n|__|\n': 8, 11 | '\n __\n|__|\n __|\n': 9, 12 | '\n __\n| |\n|__|\n': 0, 13 | '\n __\n | | |\n | |__|\n':10 14 | 15 | } 16 | return numeros.get(numero, None) 17 | -------------------------------------------------------------------------------- /2011_02_08/test_ocr.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from ocr import ocr 3 | 4 | 5 | class OCRTest(unittest.TestCase): 6 | 7 | def test_numero_um(self): 8 | numero_um = """ 9 | 10 | | 11 | | 12 | """ 13 | 14 | self.assertEquals(1, ocr(numero_um)) 15 | 16 | def test_numero_dois(self): 17 | numero_dois = """ 18 | __ 19 | __| 20 | |__ 21 | """ 22 | 23 | self.assertEquals(2, ocr(numero_dois)) 24 | 25 | def test_numero_tres(self): 26 | numero_tres=""" 27 | __ 28 | __| 29 | __| 30 | """ 31 | self.assertEquals(3, ocr(numero_tres)) 32 | 33 | def test_numero_quatro(self): 34 | numero_quatro=""" 35 | 36 | |__| 37 | | 38 | """ 39 | 40 | self.assertEquals(4, ocr(numero_quatro)) 41 | 42 | def test_numero_cinco(self): 43 | numero_cinco = """ 44 | __ 45 | |__ 46 | __| 47 | """ 48 | self.assertEquals(5, ocr(numero_cinco)) 49 | 50 | def test_numero_seis(self): 51 | numero_seis = """ 52 | __ 53 | |__ 54 | |__| 55 | """ 56 | self.assertEquals(6, ocr(numero_seis)) 57 | 58 | def test_numero_sete(self): 59 | numero_sete = """ 60 | __ 61 | | 62 | | 63 | """ 64 | self.assertEquals(7, ocr(numero_sete)) 65 | 66 | def test_numero_oito(self): 67 | numero_oito = """ 68 | __ 69 | |__| 70 | |__| 71 | """ 72 | self.assertEquals(8, ocr(numero_oito)) 73 | 74 | def test_numero_nove(self): 75 | numero_nove = """ 76 | __ 77 | |__| 78 | __| 79 | """ 80 | self.assertEquals(9, ocr(numero_nove)) 81 | 82 | def test_numero_zero(self): 83 | numero_zero = """ 84 | __ 85 | | | 86 | |__| 87 | """ 88 | self.assertEquals(0, ocr(numero_zero)) 89 | 90 | def test_numero_dez(self): 91 | numero_dez = """ 92 | __ 93 | | | | 94 | | |__| 95 | """ 96 | print repr(numero_dez) 97 | self.assertEquals(10, ocr(numero_dez)) 98 | 99 | if __name__ == '__main__': 100 | unittest.main() -------------------------------------------------------------------------------- /2011_02_15/PARTICIPANTES: -------------------------------------------------------------------------------- 1 | 1.Tarsis Azevedo - tarsis 2 | 2.Hugo Lopes - hugobr 3 | 3.Andrews Medina - andrewsmedina 4 | 4.Fabio M. Costa - fabiomcosta 5 | 5.Vinicius Mendes - vbmendes 6 | 6.Alberto Beloni - albertobeloni 7 | 7.Gustavo Luz - gustavoluz 8 | 8.Danilo Moret - moret 9 | 9.Francisco - franciscosouza 10 | 10.Carlos Laviola - claviola 11 | 11.Alvaro Justen - turicas 12 | 12.Tatiana - tatiana -------------------------------------------------------------------------------- /2011_02_15/RETROSPECTIVA: -------------------------------------------------------------------------------- 1 | Bom ;D 2 | ------ 3 | *Exercicio ++ 4 | *Todo mundo programou 5 | *Vai ter pizza no proximo dojo 6 | *Galera legal 7 | *Gente de varios times 8 | *Intrusos ;D 9 | 10 | Ruim ;( 11 | ------- 12 | *Sala ocupada 13 | *Nao teve baby step 14 | *Interferencia externa 15 | *Nao teve comida 16 | *Teclado do mac 17 | *TextMate 18 | *Andrews trollou timer 19 | 20 | Melhorar 21 | -------- 22 | *Falar menos 23 | *Nao pode casais perto ++ 24 | *Timer melhor -------------------------------------------------------------------------------- /2011_02_15/test_wordwrap.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from wordwrap import wrap 3 | 4 | class WordWrapTest(unittest.TestCase): 5 | 6 | def test_quebra_de_linha_no_fim_de_uma_palavra(self): 7 | self.assertEquals(wrap('word', 5), 'word') 8 | 9 | def test_quebra_de_linha_com_duas_palavras_que_excedem_o_tamanho_da_coluna(self): 10 | self.assertEquals(wrap('two words', 4), 'two\nwords') 11 | 12 | def test_palavra_maior_que_tamanho_da_coluna(self): 13 | self.assertEquals(wrap('bigword', 4), 'bigword') 14 | 15 | def test_quebra_de_linha_com_duas_palavras_maiores_que_o_tamanho_da_coluna(self): 16 | self.assertEquals(wrap('bigword bigword', 4), 'bigword\nbigword') 17 | 18 | def test_palavras_menores_que_tamanho_da_coluna(self): 19 | self.assertEquals(wrap('big word', 15), 'big word') 20 | 21 | def test_tres_palavras_com_duas_com_tamanho_menor_que_a_quantidade_de_colunas(self): 22 | self.assertEquals(wrap('quer misto quente?', 10), 'quer misto\nquente?') 23 | 24 | def test_tres_linhas(self): 25 | self.assertEquals(wrap('loren loren loren', 3), 'loren\nloren\nloren') 26 | 27 | def test_tres_linhas_com_espaco_na_primeira_linha(self): 28 | self.assertEquals(wrap('loren loren loren loren loren loren', 11), 'loren loren\nloren loren\nloren loren') 29 | 30 | def test_tres_linhas_com_mais_de_um_espaco_entre_palavras(self): 31 | self.assertEquals(wrap('loren loren', 7), 'loren\nloren') 32 | 33 | def test_duas_linhas_com_barra_n(self): 34 | self.assertEquals(wrap('loren\nloren loren', 11), 'loren\nloren loren') 35 | 36 | 37 | unittest.main() -------------------------------------------------------------------------------- /2011_02_15/wordwrap.py: -------------------------------------------------------------------------------- 1 | def wrap(palavras, colunas): 2 | if len(palavras) < colunas: 3 | return palavras 4 | i = 0 5 | resultado = [] 6 | for palavra in palavras.split(): 7 | i += len(palavra) 8 | if len(resultado) > 0: 9 | if i < colunas: 10 | resultado.append(' ') 11 | resultado.append(palavra) 12 | i += 1 13 | else: 14 | resultado.append('\n') 15 | resultado.append(palavra) 16 | i = 0 17 | else: 18 | resultado.append(palavra) 19 | 20 | return ''.join(resultado) -------------------------------------------------------------------------------- /2011_02_22/PARTICIPANTES: -------------------------------------------------------------------------------- 1 | PARTICIPANTES: 2 | - Fábio 3 | - Thales 4 | - Andrews 5 | - Chico 6 | - Quixadá 7 | - Rômulo 8 | - Davidson 9 | - Evandro 10 | -------------------------------------------------------------------------------- /2011_02_22/README: -------------------------------------------------------------------------------- 1 | README 2 | 3 | O problema consiste em criar um plugin jQuery de autocomplete usando BDD e a ferramenta de testes Jasmine. 4 | 5 | A versão do plugin funcionando se encontra na pasta "demo/" e o framework já ficou montado na pasta "tests/". 6 | 7 | -------------------------------------------------------------------------------- /2011_02_22/RETROSPECTIVA: -------------------------------------------------------------------------------- 1 | :) 2 | 3 | - Interação entre as castas 4 | - Aprendizagem de javascript 5 | - Problema legal e real 6 | - Prática de BDD em Javascript 7 | - Conhecer Jasmine 8 | - Pouca gente 9 | - Gente nova 10 | - Experiência com dojo 11 | - Introdução à respeito da dinâmica (dojo) 12 | 13 | 14 | :( 15 | 16 | - Programar pouco é um problema 17 | - Anunciar a linguagem do dojo 18 | - Galera não pediu ajuda no começo da dinâmica 19 | - FALTOU A PORRA DA PIZZA!!!!!! 20 | 21 | 22 | SUGESTÕES: 23 | 24 | - Dois ou mais problemas rolando em grupos ao mesmo tempo 25 | - Fazer dojo com Node.js 26 | - Fazer dojo com Javascript puro 27 | - Fazer dojo que não é de programação: Photoshop 28 | 29 | -------------------------------------------------------------------------------- /2011_02_22/demo/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hltbra/dojo_globocom/022ab356a1dc9f35131cbc96d2b266d5bb9142a4/2011_02_22/demo/bg.gif -------------------------------------------------------------------------------- /2011_02_22/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Autocomplete Plugin 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 |

jQuery Autocomplete Plugin

25 | 26 |
27 | 28 |
29 |

30 | 31 | 32 |

33 | 34 | 35 |
36 | 37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /2011_02_22/demo/jquery.autocomplete.css: -------------------------------------------------------------------------------- 1 | .ac_results { 2 | padding: 0px; 3 | border: 1px solid black; 4 | background-color: white; 5 | overflow: hidden; 6 | z-index: 99999; 7 | } 8 | 9 | .ac_results ul { 10 | width: 100%; 11 | list-style-position: outside; 12 | list-style: none; 13 | padding: 0; 14 | margin: 0; 15 | } 16 | 17 | .ac_results li { 18 | margin: 0px; 19 | padding: 2px 5px; 20 | cursor: default; 21 | display: block; 22 | /* 23 | if width will be 100% horizontal scrollbar will apear 24 | when scroll mode will be used 25 | */ 26 | /*width: 100%;*/ 27 | font: menu; 28 | font-size: 12px; 29 | /* 30 | it is very important, if line-height not setted or setted 31 | in relative units scroll will be broken in firefox 32 | */ 33 | line-height: 16px; 34 | overflow: hidden; 35 | } 36 | 37 | .ac_loading { 38 | background: white url('indicator.gif') right center no-repeat; 39 | } 40 | 41 | .ac_odd { 42 | background-color: #eee; 43 | } 44 | 45 | .ac_over { 46 | background-color: #0A246A; 47 | color: white; 48 | } 49 | -------------------------------------------------------------------------------- /2011_02_22/demo/jquery.autocomplete.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Autocomplete plugin 1.1 3 | * 4 | * Copyright (c) 2009 Jörn Zaefferer 5 | * 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * 10 | * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $ 11 | */ 12 | 13 | ;(function($) { 14 | 15 | $.fn.extend({ 16 | autocomplete: function(urlOrData, options) { 17 | var isUrl = typeof urlOrData == "string"; 18 | options = $.extend({}, $.Autocompleter.defaults, { 19 | url: isUrl ? urlOrData : null, 20 | data: isUrl ? null : urlOrData, 21 | delay: isUrl ? $.Autocompleter.defaults.delay : 10, 22 | max: options && !options.scroll ? 10 : 150 23 | }, options); 24 | 25 | // if highlight is set to false, replace it with a do-nothing function 26 | options.highlight = options.highlight || function(value) { return value; }; 27 | 28 | // if the formatMatch option is not specified, then use formatItem for backwards compatibility 29 | options.formatMatch = options.formatMatch || options.formatItem; 30 | 31 | return this.each(function() { 32 | new $.Autocompleter(this, options); 33 | }); 34 | }, 35 | result: function(handler) { 36 | return this.bind("result", handler); 37 | }, 38 | search: function(handler) { 39 | return this.trigger("search", [handler]); 40 | }, 41 | flushCache: function() { 42 | return this.trigger("flushCache"); 43 | }, 44 | setOptions: function(options){ 45 | return this.trigger("setOptions", [options]); 46 | }, 47 | unautocomplete: function() { 48 | return this.trigger("unautocomplete"); 49 | } 50 | }); 51 | 52 | $.Autocompleter = function(input, options) { 53 | 54 | var KEY = { 55 | UP: 38, 56 | DOWN: 40, 57 | DEL: 46, 58 | TAB: 9, 59 | RETURN: 13, 60 | ESC: 27, 61 | COMMA: 188, 62 | PAGEUP: 33, 63 | PAGEDOWN: 34, 64 | BACKSPACE: 8 65 | }; 66 | 67 | // Create $ object for input element 68 | var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); 69 | 70 | var timeout; 71 | var previousValue = ""; 72 | var cache = $.Autocompleter.Cache(options); 73 | var hasFocus = 0; 74 | var lastKeyPressCode; 75 | var config = { 76 | mouseDownOnSelect: false 77 | }; 78 | var select = $.Autocompleter.Select(options, input, selectCurrent, config); 79 | 80 | var blockSubmit; 81 | 82 | // prevent form submit in opera when selecting with return key 83 | $.browser.opera && $(input.form).bind("submit.autocomplete", function() { 84 | if (blockSubmit) { 85 | blockSubmit = false; 86 | return false; 87 | } 88 | }); 89 | 90 | // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all 91 | $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { 92 | // a keypress means the input has focus 93 | // avoids issue where input had focus before the autocomplete was applied 94 | hasFocus = 1; 95 | // track last key pressed 96 | lastKeyPressCode = event.keyCode; 97 | switch(event.keyCode) { 98 | 99 | case KEY.UP: 100 | event.preventDefault(); 101 | if ( select.visible() ) { 102 | select.prev(); 103 | } else { 104 | onChange(0, true); 105 | } 106 | break; 107 | 108 | case KEY.DOWN: 109 | event.preventDefault(); 110 | if ( select.visible() ) { 111 | select.next(); 112 | } else { 113 | onChange(0, true); 114 | } 115 | break; 116 | 117 | case KEY.PAGEUP: 118 | event.preventDefault(); 119 | if ( select.visible() ) { 120 | select.pageUp(); 121 | } else { 122 | onChange(0, true); 123 | } 124 | break; 125 | 126 | case KEY.PAGEDOWN: 127 | event.preventDefault(); 128 | if ( select.visible() ) { 129 | select.pageDown(); 130 | } else { 131 | onChange(0, true); 132 | } 133 | break; 134 | 135 | // matches also semicolon 136 | case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: 137 | case KEY.TAB: 138 | case KEY.RETURN: 139 | if( selectCurrent() ) { 140 | // stop default to prevent a form submit, Opera needs special handling 141 | event.preventDefault(); 142 | blockSubmit = true; 143 | return false; 144 | } 145 | break; 146 | 147 | case KEY.ESC: 148 | select.hide(); 149 | break; 150 | 151 | default: 152 | clearTimeout(timeout); 153 | timeout = setTimeout(onChange, options.delay); 154 | break; 155 | } 156 | }).focus(function(){ 157 | // track whether the field has focus, we shouldn't process any 158 | // results if the field no longer has focus 159 | hasFocus++; 160 | }).blur(function() { 161 | hasFocus = 0; 162 | if (!config.mouseDownOnSelect) { 163 | hideResults(); 164 | } 165 | }).click(function() { 166 | // show select when clicking in a focused field 167 | if ( hasFocus++ > 1 && !select.visible() ) { 168 | onChange(0, true); 169 | } 170 | }).bind("search", function() { 171 | // TODO why not just specifying both arguments? 172 | var fn = (arguments.length > 1) ? arguments[1] : null; 173 | function findValueCallback(q, data) { 174 | var result; 175 | if( data && data.length ) { 176 | for (var i=0; i < data.length; i++) { 177 | if( data[i].result.toLowerCase() == q.toLowerCase() ) { 178 | result = data[i]; 179 | break; 180 | } 181 | } 182 | } 183 | if( typeof fn == "function" ) fn(result); 184 | else $input.trigger("result", result && [result.data, result.value]); 185 | } 186 | $.each(trimWords($input.val()), function(i, value) { 187 | request(value, findValueCallback, findValueCallback); 188 | }); 189 | }).bind("flushCache", function() { 190 | cache.flush(); 191 | }).bind("setOptions", function() { 192 | $.extend(options, arguments[1]); 193 | // if we've updated the data, repopulate 194 | if ( "data" in arguments[1] ) 195 | cache.populate(); 196 | }).bind("unautocomplete", function() { 197 | select.unbind(); 198 | $input.unbind(); 199 | $(input.form).unbind(".autocomplete"); 200 | }); 201 | 202 | 203 | function selectCurrent() { 204 | var selected = select.selected(); 205 | if( !selected ) 206 | return false; 207 | 208 | var v = selected.result; 209 | previousValue = v; 210 | 211 | if ( options.multiple ) { 212 | var words = trimWords($input.val()); 213 | if ( words.length > 1 ) { 214 | var seperator = options.multipleSeparator.length; 215 | var cursorAt = $(input).selection().start; 216 | var wordAt, progress = 0; 217 | $.each(words, function(i, word) { 218 | progress += word.length; 219 | if (cursorAt <= progress) { 220 | wordAt = i; 221 | return false; 222 | } 223 | progress += seperator; 224 | }); 225 | words[wordAt] = v; 226 | // TODO this should set the cursor to the right position, but it gets overriden somewhere 227 | //$.Autocompleter.Selection(input, progress + seperator, progress + seperator); 228 | v = words.join( options.multipleSeparator ); 229 | } 230 | v += options.multipleSeparator; 231 | } 232 | 233 | $input.val(v); 234 | hideResultsNow(); 235 | $input.trigger("result", [selected.data, selected.value]); 236 | return true; 237 | } 238 | 239 | function onChange(crap, skipPrevCheck) { 240 | if( lastKeyPressCode == KEY.DEL ) { 241 | select.hide(); 242 | return; 243 | } 244 | 245 | var currentValue = $input.val(); 246 | 247 | if ( !skipPrevCheck && currentValue == previousValue ) 248 | return; 249 | 250 | previousValue = currentValue; 251 | 252 | currentValue = lastWord(currentValue); 253 | if ( currentValue.length >= options.minChars) { 254 | $input.addClass(options.loadingClass); 255 | if (!options.matchCase) 256 | currentValue = currentValue.toLowerCase(); 257 | request(currentValue, receiveData, hideResultsNow); 258 | } else { 259 | stopLoading(); 260 | select.hide(); 261 | } 262 | }; 263 | 264 | function trimWords(value) { 265 | if (!value) 266 | return [""]; 267 | if (!options.multiple) 268 | return [$.trim(value)]; 269 | return $.map(value.split(options.multipleSeparator), function(word) { 270 | return $.trim(value).length ? $.trim(word) : null; 271 | }); 272 | } 273 | 274 | function lastWord(value) { 275 | if ( !options.multiple ) 276 | return value; 277 | var words = trimWords(value); 278 | if (words.length == 1) 279 | return words[0]; 280 | var cursorAt = $(input).selection().start; 281 | if (cursorAt == value.length) { 282 | words = trimWords(value) 283 | } else { 284 | words = trimWords(value.replace(value.substring(cursorAt), "")); 285 | } 286 | return words[words.length - 1]; 287 | } 288 | 289 | // fills in the input box w/the first match (assumed to be the best match) 290 | // q: the term entered 291 | // sValue: the first matching result 292 | function autoFill(q, sValue){ 293 | // autofill in the complete box w/the first match as long as the user hasn't entered in more data 294 | // if the last user key pressed was backspace, don't autofill 295 | if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { 296 | // fill in the value (keep the case the user has typed) 297 | $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); 298 | // select the portion of the value not typed by the user (so the next character will erase) 299 | $(input).selection(previousValue.length, previousValue.length + sValue.length); 300 | } 301 | }; 302 | 303 | function hideResults() { 304 | clearTimeout(timeout); 305 | timeout = setTimeout(hideResultsNow, 200); 306 | }; 307 | 308 | function hideResultsNow() { 309 | var wasVisible = select.visible(); 310 | select.hide(); 311 | clearTimeout(timeout); 312 | stopLoading(); 313 | if (options.mustMatch) { 314 | // call search and run callback 315 | $input.search( 316 | function (result){ 317 | // if no value found, clear the input box 318 | if( !result ) { 319 | if (options.multiple) { 320 | var words = trimWords($input.val()).slice(0, -1); 321 | $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); 322 | } 323 | else { 324 | $input.val( "" ); 325 | $input.trigger("result", null); 326 | } 327 | } 328 | } 329 | ); 330 | } 331 | }; 332 | 333 | function receiveData(q, data) { 334 | if ( data && data.length && hasFocus ) { 335 | stopLoading(); 336 | select.display(data, q); 337 | autoFill(q, data[0].value); 338 | select.show(); 339 | } else { 340 | hideResultsNow(); 341 | } 342 | }; 343 | 344 | function request(term, success, failure) { 345 | if (!options.matchCase) 346 | term = term.toLowerCase(); 347 | var data = cache.load(term); 348 | // recieve the cached data 349 | if (data && data.length) { 350 | success(term, data); 351 | // if an AJAX url has been supplied, try loading the data now 352 | } else if( (typeof options.url == "string") && (options.url.length > 0) ){ 353 | 354 | var extraParams = { 355 | timestamp: +new Date() 356 | }; 357 | $.each(options.extraParams, function(key, param) { 358 | extraParams[key] = typeof param == "function" ? param() : param; 359 | }); 360 | 361 | $.ajax({ 362 | // try to leverage ajaxQueue plugin to abort previous requests 363 | mode: "abort", 364 | // limit abortion to this input 365 | port: "autocomplete" + input.name, 366 | dataType: options.dataType, 367 | url: options.url, 368 | data: $.extend({ 369 | q: lastWord(term), 370 | limit: options.max 371 | }, extraParams), 372 | success: function(data) { 373 | var parsed = options.parse && options.parse(data) || parse(data); 374 | cache.add(term, parsed); 375 | success(term, parsed); 376 | } 377 | }); 378 | } else { 379 | // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match 380 | select.emptyList(); 381 | failure(term); 382 | } 383 | }; 384 | 385 | function parse(data) { 386 | var parsed = []; 387 | var rows = data.split("\n"); 388 | for (var i=0; i < rows.length; i++) { 389 | var row = $.trim(rows[i]); 390 | if (row) { 391 | row = row.split("|"); 392 | parsed[parsed.length] = { 393 | data: row, 394 | value: row[0], 395 | result: options.formatResult && options.formatResult(row, row[0]) || row[0] 396 | }; 397 | } 398 | } 399 | return parsed; 400 | }; 401 | 402 | function stopLoading() { 403 | $input.removeClass(options.loadingClass); 404 | }; 405 | 406 | }; 407 | 408 | $.Autocompleter.defaults = { 409 | inputClass: "ac_input", 410 | resultsClass: "ac_results", 411 | loadingClass: "ac_loading", 412 | minChars: 1, 413 | delay: 400, 414 | matchCase: false, 415 | matchSubset: true, 416 | matchContains: false, 417 | cacheLength: 10, 418 | max: 100, 419 | mustMatch: false, 420 | extraParams: {}, 421 | selectFirst: true, 422 | formatItem: function(row) { return row[0]; }, 423 | formatMatch: null, 424 | autoFill: false, 425 | width: 0, 426 | multiple: false, 427 | multipleSeparator: ", ", 428 | highlight: function(value, term) { 429 | return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); 430 | }, 431 | scroll: true, 432 | scrollHeight: 180 433 | }; 434 | 435 | $.Autocompleter.Cache = function(options) { 436 | 437 | var data = {}; 438 | var length = 0; 439 | 440 | function matchSubset(s, sub) { 441 | if (!options.matchCase) 442 | s = s.toLowerCase(); 443 | var i = s.indexOf(sub); 444 | if (options.matchContains == "word"){ 445 | i = s.toLowerCase().search("\\b" + sub.toLowerCase()); 446 | } 447 | if (i == -1) return false; 448 | return i == 0 || options.matchContains; 449 | }; 450 | 451 | function add(q, value) { 452 | if (length > options.cacheLength){ 453 | flush(); 454 | } 455 | if (!data[q]){ 456 | length++; 457 | } 458 | data[q] = value; 459 | } 460 | 461 | function populate(){ 462 | if( !options.data ) return false; 463 | // track the matches 464 | var stMatchSets = {}, 465 | nullData = 0; 466 | 467 | // no url was specified, we need to adjust the cache length to make sure it fits the local data store 468 | if( !options.url ) options.cacheLength = 1; 469 | 470 | // track all options for minChars = 0 471 | stMatchSets[""] = []; 472 | 473 | // loop through the array and create a lookup structure 474 | for ( var i = 0, ol = options.data.length; i < ol; i++ ) { 475 | var rawValue = options.data[i]; 476 | // if rawValue is a string, make an array otherwise just reference the array 477 | rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; 478 | 479 | var value = options.formatMatch(rawValue, i+1, options.data.length); 480 | if ( value === false ) 481 | continue; 482 | 483 | var firstChar = value.charAt(0).toLowerCase(); 484 | // if no lookup array for this character exists, look it up now 485 | if( !stMatchSets[firstChar] ) 486 | stMatchSets[firstChar] = []; 487 | 488 | // if the match is a string 489 | var row = { 490 | value: value, 491 | data: rawValue, 492 | result: options.formatResult && options.formatResult(rawValue) || value 493 | }; 494 | 495 | // push the current match into the set list 496 | stMatchSets[firstChar].push(row); 497 | 498 | // keep track of minChars zero items 499 | if ( nullData++ < options.max ) { 500 | stMatchSets[""].push(row); 501 | } 502 | }; 503 | 504 | // add the data items to the cache 505 | $.each(stMatchSets, function(i, value) { 506 | // increase the cache size 507 | options.cacheLength++; 508 | // add to the cache 509 | add(i, value); 510 | }); 511 | } 512 | 513 | // populate any existing data 514 | setTimeout(populate, 25); 515 | 516 | function flush(){ 517 | data = {}; 518 | length = 0; 519 | } 520 | 521 | return { 522 | flush: flush, 523 | add: add, 524 | populate: populate, 525 | load: function(q) { 526 | if (!options.cacheLength || !length) 527 | return null; 528 | /* 529 | * if dealing w/local data and matchContains than we must make sure 530 | * to loop through all the data collections looking for matches 531 | */ 532 | if( !options.url && options.matchContains ){ 533 | // track all matches 534 | var csub = []; 535 | // loop through all the data grids for matches 536 | for( var k in data ){ 537 | // don't search through the stMatchSets[""] (minChars: 0) cache 538 | // this prevents duplicates 539 | if( k.length > 0 ){ 540 | var c = data[k]; 541 | $.each(c, function(i, x) { 542 | // if we've got a match, add it to the array 543 | if (matchSubset(x.value, q)) { 544 | csub.push(x); 545 | } 546 | }); 547 | } 548 | } 549 | return csub; 550 | } else 551 | // if the exact item exists, use it 552 | if (data[q]){ 553 | return data[q]; 554 | } else 555 | if (options.matchSubset) { 556 | for (var i = q.length - 1; i >= options.minChars; i--) { 557 | var c = data[q.substr(0, i)]; 558 | if (c) { 559 | var csub = []; 560 | $.each(c, function(i, x) { 561 | if (matchSubset(x.value, q)) { 562 | csub[csub.length] = x; 563 | } 564 | }); 565 | return csub; 566 | } 567 | } 568 | } 569 | return null; 570 | } 571 | }; 572 | }; 573 | 574 | $.Autocompleter.Select = function (options, input, select, config) { 575 | var CLASSES = { 576 | ACTIVE: "ac_over" 577 | }; 578 | 579 | var listItems, 580 | active = -1, 581 | data, 582 | term = "", 583 | needsInit = true, 584 | element, 585 | list; 586 | 587 | // Create results 588 | function init() { 589 | if (!needsInit) 590 | return; 591 | element = $("
") 592 | .hide() 593 | .addClass(options.resultsClass) 594 | .css("position", "absolute") 595 | .appendTo(document.body); 596 | 597 | list = $("