├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.rdoc ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── rails.png │ │ ├── select2-spinner.gif │ │ ├── select2.png │ │ └── select2x2.png │ ├── javascripts │ │ ├── application.js │ │ ├── bootstrap.js.coffee │ │ ├── people.js.coffee │ │ ├── prettify.css │ │ ├── prettify.js │ │ └── select2.js │ └── stylesheets │ │ ├── application.css │ │ ├── bootstrap_and_overrides.css.less │ │ ├── people.css.scss │ │ ├── prettify.css │ │ └── select2.css ├── controllers │ ├── application_controller.rb │ └── people_controller.rb ├── helpers │ ├── application_helper.rb │ └── people_helper.rb ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ └── person.rb └── views │ ├── layouts │ └── application.html.erb │ └── people │ └── index.html.erb ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── backtrace_silencers.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── secret_token.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── en.bootstrap.yml │ └── en.yml └── routes.rb ├── db ├── migrate │ └── 20130603161159_create_people.rb ├── schema.rb └── seeds.rb ├── doc └── README_FOR_APP ├── lib ├── assets │ └── .gitkeep └── tasks │ └── .gitkeep ├── log └── .gitkeep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── script └── rails ├── test ├── fixtures │ ├── .gitkeep │ └── people.yml ├── functional │ ├── .gitkeep │ └── people_controller_test.rb ├── integration │ └── .gitkeep ├── performance │ └── browsing_test.rb ├── test_helper.rb └── unit │ ├── .gitkeep │ ├── helpers │ └── people_helper_test.rb │ └── person_test.rb └── vendor ├── assets ├── javascripts │ └── .gitkeep └── stylesheets │ └── .gitkeep └── plugins └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/*.log 15 | /tmp 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '3.2.12' 4 | gem 'twitter-bootstrap-rails' 5 | gem 'jquery-rails' 6 | 7 | group :assets do 8 | gem 'sass-rails', '~> 3.2.3' 9 | gem 'coffee-rails', '~> 3.2.1' 10 | gem 'less-rails' 11 | gem 'therubyracer' 12 | gem 'uglifier', '>= 1.0.3' 13 | end 14 | 15 | group :development do 16 | gem 'sqlite3' 17 | end 18 | 19 | group :production do 20 | gem 'pg' 21 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionmailer (3.2.12) 5 | actionpack (= 3.2.12) 6 | mail (~> 2.4.4) 7 | actionpack (3.2.12) 8 | activemodel (= 3.2.12) 9 | activesupport (= 3.2.12) 10 | builder (~> 3.0.0) 11 | erubis (~> 2.7.0) 12 | journey (~> 1.0.4) 13 | rack (~> 1.4.5) 14 | rack-cache (~> 1.2) 15 | rack-test (~> 0.6.1) 16 | sprockets (~> 2.2.1) 17 | activemodel (3.2.12) 18 | activesupport (= 3.2.12) 19 | builder (~> 3.0.0) 20 | activerecord (3.2.12) 21 | activemodel (= 3.2.12) 22 | activesupport (= 3.2.12) 23 | arel (~> 3.0.2) 24 | tzinfo (~> 0.3.29) 25 | activeresource (3.2.12) 26 | activemodel (= 3.2.12) 27 | activesupport (= 3.2.12) 28 | activesupport (3.2.12) 29 | i18n (~> 0.6) 30 | multi_json (~> 1.0) 31 | arel (3.0.2) 32 | builder (3.0.4) 33 | coffee-rails (3.2.2) 34 | coffee-script (>= 2.2.0) 35 | railties (~> 3.2.0) 36 | coffee-script (2.2.0) 37 | coffee-script-source 38 | execjs 39 | coffee-script-source (1.6.2) 40 | commonjs (0.2.6) 41 | erubis (2.7.0) 42 | execjs (1.4.0) 43 | multi_json (~> 1.0) 44 | hike (1.2.2) 45 | i18n (0.6.4) 46 | journey (1.0.4) 47 | jquery-rails (3.0.0) 48 | railties (>= 3.0, < 5.0) 49 | thor (>= 0.14, < 2.0) 50 | json (1.8.0) 51 | less (2.3.2) 52 | commonjs (~> 0.2.6) 53 | less-rails (2.3.3) 54 | actionpack (>= 3.1) 55 | less (~> 2.3.1) 56 | libv8 (3.11.8.17) 57 | mail (2.4.4) 58 | i18n (>= 0.4.0) 59 | mime-types (~> 1.16) 60 | treetop (~> 1.4.8) 61 | mime-types (1.23) 62 | multi_json (1.7.5) 63 | pg (0.14.1) 64 | polyglot (0.3.3) 65 | rack (1.4.5) 66 | rack-cache (1.2) 67 | rack (>= 0.4) 68 | rack-ssl (1.3.3) 69 | rack 70 | rack-test (0.6.2) 71 | rack (>= 1.0) 72 | rails (3.2.12) 73 | actionmailer (= 3.2.12) 74 | actionpack (= 3.2.12) 75 | activerecord (= 3.2.12) 76 | activeresource (= 3.2.12) 77 | activesupport (= 3.2.12) 78 | bundler (~> 1.0) 79 | railties (= 3.2.12) 80 | railties (3.2.12) 81 | actionpack (= 3.2.12) 82 | activesupport (= 3.2.12) 83 | rack-ssl (~> 1.3.2) 84 | rake (>= 0.8.7) 85 | rdoc (~> 3.4) 86 | thor (>= 0.14.6, < 2.0) 87 | rake (10.0.4) 88 | rdoc (3.12.2) 89 | json (~> 1.4) 90 | ref (1.0.5) 91 | sass (3.2.9) 92 | sass-rails (3.2.6) 93 | railties (~> 3.2.0) 94 | sass (>= 3.1.10) 95 | tilt (~> 1.3) 96 | sprockets (2.2.2) 97 | hike (~> 1.2) 98 | multi_json (~> 1.0) 99 | rack (~> 1.0) 100 | tilt (~> 1.1, != 1.3.0) 101 | sqlite3 (1.3.7) 102 | therubyracer (0.11.4) 103 | libv8 (~> 3.11.8.12) 104 | ref 105 | thor (0.18.1) 106 | tilt (1.4.1) 107 | treetop (1.4.12) 108 | polyglot 109 | polyglot (>= 0.3.1) 110 | twitter-bootstrap-rails (2.2.6) 111 | actionpack (>= 3.1) 112 | execjs 113 | railties (>= 3.1) 114 | tzinfo (0.3.37) 115 | uglifier (2.1.1) 116 | execjs (>= 0.3.0) 117 | multi_json (~> 1.0, >= 1.0.2) 118 | 119 | PLATFORMS 120 | ruby 121 | 122 | DEPENDENCIES 123 | coffee-rails (~> 3.2.1) 124 | jquery-rails 125 | less-rails 126 | pg 127 | rails (= 3.2.12) 128 | sass-rails (~> 3.2.3) 129 | sqlite3 130 | therubyracer 131 | twitter-bootstrap-rails 132 | uglifier (>= 1.0.3) 133 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | == Ruby on Rails with Select2 Example Application -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Select2::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/app/assets/images/rails.png -------------------------------------------------------------------------------- /app/assets/images/select2-spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/app/assets/images/select2-spinner.gif -------------------------------------------------------------------------------- /app/assets/images/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/app/assets/images/select2.png -------------------------------------------------------------------------------- /app/assets/images/select2x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/app/assets/images/select2x2.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require twitter/bootstrap 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap.js.coffee: -------------------------------------------------------------------------------- 1 | jQuery -> 2 | $("a[rel=popover]").popover() 3 | $(".tooltip").tooltip() 4 | $("a[rel=tooltip]").tooltip() -------------------------------------------------------------------------------- /app/assets/javascripts/people.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 20px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /app/assets/javascripts/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p= 112 && k <= 123; 97 | } 98 | }, 99 | MEASURE_SCROLLBAR_TEMPLATE = "
"; 100 | 101 | $document = $(document); 102 | 103 | nextUid=(function() { var counter=1; return function() { return counter++; }; }()); 104 | 105 | function indexOf(value, array) { 106 | var i = 0, l = array.length; 107 | for (; i < l; i = i + 1) { 108 | if (equal(value, array[i])) return i; 109 | } 110 | return -1; 111 | } 112 | 113 | function measureScrollbar () { 114 | var $template = $( MEASURE_SCROLLBAR_TEMPLATE ); 115 | $template.appendTo('body'); 116 | 117 | var dim = { 118 | width: $template.width() - $template[0].clientWidth, 119 | height: $template.height() - $template[0].clientHeight 120 | }; 121 | $template.remove(); 122 | 123 | return dim; 124 | } 125 | 126 | /** 127 | * Compares equality of a and b 128 | * @param a 129 | * @param b 130 | */ 131 | function equal(a, b) { 132 | if (a === b) return true; 133 | if (a === undefined || b === undefined) return false; 134 | if (a === null || b === null) return false; 135 | if (a.constructor === String) return a+'' === b+''; // IE requires a+'' instead of just a 136 | if (b.constructor === String) return b+'' === a+''; // IE requires b+'' instead of just b 137 | return false; 138 | } 139 | 140 | /** 141 | * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty 142 | * strings 143 | * @param string 144 | * @param separator 145 | */ 146 | function splitVal(string, separator) { 147 | var val, i, l; 148 | if (string === null || string.length < 1) return []; 149 | val = string.split(separator); 150 | for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]); 151 | return val; 152 | } 153 | 154 | function getSideBorderPadding(element) { 155 | return element.outerWidth(false) - element.width(); 156 | } 157 | 158 | function installKeyUpChangeEvent(element) { 159 | var key="keyup-change-value"; 160 | element.on("keydown", function () { 161 | if ($.data(element, key) === undefined) { 162 | $.data(element, key, element.val()); 163 | } 164 | }); 165 | element.on("keyup", function () { 166 | var val= $.data(element, key); 167 | if (val !== undefined && element.val() !== val) { 168 | $.removeData(element, key); 169 | element.trigger("keyup-change"); 170 | } 171 | }); 172 | } 173 | 174 | $document.on("mousemove", function (e) { 175 | lastMousePosition = {x: e.pageX, y: e.pageY}; 176 | }); 177 | 178 | /** 179 | * filters mouse events so an event is fired only if the mouse moved. 180 | * 181 | * filters out mouse events that occur when mouse is stationary but 182 | * the elements under the pointer are scrolled. 183 | */ 184 | function installFilteredMouseMove(element) { 185 | element.on("mousemove", function (e) { 186 | var lastpos = lastMousePosition; 187 | if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) { 188 | $(e.target).trigger("mousemove-filtered", e); 189 | } 190 | }); 191 | } 192 | 193 | /** 194 | * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made 195 | * within the last quietMillis milliseconds. 196 | * 197 | * @param quietMillis number of milliseconds to wait before invoking fn 198 | * @param fn function to be debounced 199 | * @param ctx object to be used as this reference within fn 200 | * @return debounced version of fn 201 | */ 202 | function debounce(quietMillis, fn, ctx) { 203 | ctx = ctx || undefined; 204 | var timeout; 205 | return function () { 206 | var args = arguments; 207 | window.clearTimeout(timeout); 208 | timeout = window.setTimeout(function() { 209 | fn.apply(ctx, args); 210 | }, quietMillis); 211 | }; 212 | } 213 | 214 | /** 215 | * A simple implementation of a thunk 216 | * @param formula function used to lazily initialize the thunk 217 | * @return {Function} 218 | */ 219 | function thunk(formula) { 220 | var evaluated = false, 221 | value; 222 | return function() { 223 | if (evaluated === false) { value = formula(); evaluated = true; } 224 | return value; 225 | }; 226 | }; 227 | 228 | function installDebouncedScroll(threshold, element) { 229 | var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);}); 230 | element.on("scroll", function (e) { 231 | if (indexOf(e.target, element.get()) >= 0) notify(e); 232 | }); 233 | } 234 | 235 | function focus($el) { 236 | if ($el[0] === document.activeElement) return; 237 | 238 | /* set the focus in a 0 timeout - that way the focus is set after the processing 239 | of the current event has finished - which seems like the only reliable way 240 | to set focus */ 241 | window.setTimeout(function() { 242 | var el=$el[0], pos=$el.val().length, range; 243 | 244 | $el.focus(); 245 | 246 | /* make sure el received focus so we do not error out when trying to manipulate the caret. 247 | sometimes modals or others listeners may steal it after its set */ 248 | if ($el.is(":visible") && el === document.activeElement) { 249 | 250 | /* after the focus is set move the caret to the end, necessary when we val() 251 | just before setting focus */ 252 | if(el.setSelectionRange) 253 | { 254 | el.setSelectionRange(pos, pos); 255 | } 256 | else if (el.createTextRange) { 257 | range = el.createTextRange(); 258 | range.collapse(false); 259 | range.select(); 260 | } 261 | } 262 | }, 0); 263 | } 264 | 265 | function getCursorInfo(el) { 266 | el = $(el)[0]; 267 | var offset = 0; 268 | var length = 0; 269 | if ('selectionStart' in el) { 270 | offset = el.selectionStart; 271 | length = el.selectionEnd - offset; 272 | } else if ('selection' in document) { 273 | el.focus(); 274 | var sel = document.selection.createRange(); 275 | length = document.selection.createRange().text.length; 276 | sel.moveStart('character', -el.value.length); 277 | offset = sel.text.length - length; 278 | } 279 | return { offset: offset, length: length }; 280 | } 281 | 282 | function killEvent(event) { 283 | event.preventDefault(); 284 | event.stopPropagation(); 285 | } 286 | function killEventImmediately(event) { 287 | event.preventDefault(); 288 | event.stopImmediatePropagation(); 289 | } 290 | 291 | function measureTextWidth(e) { 292 | if (!sizer){ 293 | var style = e[0].currentStyle || window.getComputedStyle(e[0], null); 294 | sizer = $(document.createElement("div")).css({ 295 | position: "absolute", 296 | left: "-10000px", 297 | top: "-10000px", 298 | display: "none", 299 | fontSize: style.fontSize, 300 | fontFamily: style.fontFamily, 301 | fontStyle: style.fontStyle, 302 | fontWeight: style.fontWeight, 303 | letterSpacing: style.letterSpacing, 304 | textTransform: style.textTransform, 305 | whiteSpace: "nowrap" 306 | }); 307 | sizer.attr("class","select2-sizer"); 308 | $("body").append(sizer); 309 | } 310 | sizer.text(e.val()); 311 | return sizer.width(); 312 | } 313 | 314 | function syncCssClasses(dest, src, adapter) { 315 | var classes, replacements = [], adapted; 316 | 317 | classes = dest.attr("class"); 318 | if (classes) { 319 | classes = '' + classes; // for IE which returns object 320 | $(classes.split(" ")).each2(function() { 321 | if (this.indexOf("select2-") === 0) { 322 | replacements.push(this); 323 | } 324 | }); 325 | } 326 | classes = src.attr("class"); 327 | if (classes) { 328 | classes = '' + classes; // for IE which returns object 329 | $(classes.split(" ")).each2(function() { 330 | if (this.indexOf("select2-") !== 0) { 331 | adapted = adapter(this); 332 | if (adapted) { 333 | replacements.push(this); 334 | } 335 | } 336 | }); 337 | } 338 | dest.attr("class", replacements.join(" ")); 339 | } 340 | 341 | 342 | function markMatch(text, term, markup, escapeMarkup) { 343 | var match=text.toUpperCase().indexOf(term.toUpperCase()), 344 | tl=term.length; 345 | 346 | if (match<0) { 347 | markup.push(escapeMarkup(text)); 348 | return; 349 | } 350 | 351 | markup.push(escapeMarkup(text.substring(0, match))); 352 | markup.push(""); 353 | markup.push(escapeMarkup(text.substring(match, match + tl))); 354 | markup.push(""); 355 | markup.push(escapeMarkup(text.substring(match + tl, text.length))); 356 | } 357 | 358 | /** 359 | * Produces an ajax-based query function 360 | * 361 | * @param options object containing configuration paramters 362 | * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax 363 | * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax 364 | * @param options.url url for the data 365 | * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. 366 | * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified 367 | * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often 368 | * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2. 369 | * The expected format is an object containing the following keys: 370 | * results array of objects that will be used as choices 371 | * more (optional) boolean indicating whether there are more results available 372 | * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true} 373 | */ 374 | function ajax(options) { 375 | var timeout, // current scheduled but not yet executed request 376 | requestSequence = 0, // sequence used to drop out-of-order responses 377 | handler = null, 378 | quietMillis = options.quietMillis || 100, 379 | ajaxUrl = options.url, 380 | self = this; 381 | 382 | return function (query) { 383 | window.clearTimeout(timeout); 384 | timeout = window.setTimeout(function () { 385 | requestSequence += 1; // increment the sequence 386 | var requestNumber = requestSequence, // this request's sequence number 387 | data = options.data, // ajax data function 388 | url = ajaxUrl, // ajax url string or function 389 | transport = options.transport || $.fn.select2.ajaxDefaults.transport, 390 | // deprecated - to be removed in 4.0 - use params instead 391 | deprecated = { 392 | type: options.type || 'GET', // set type of request (GET or POST) 393 | cache: options.cache || false, 394 | jsonpCallback: options.jsonpCallback||undefined, 395 | dataType: options.dataType||"json" 396 | }, 397 | params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated); 398 | 399 | data = data ? data.call(self, query.term, query.page, query.context) : null; 400 | url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url; 401 | 402 | if( null !== handler) { handler.abort(); } 403 | 404 | if (options.params) { 405 | if ($.isFunction(options.params)) { 406 | $.extend(params, options.params.call(self)); 407 | } else { 408 | $.extend(params, options.params); 409 | } 410 | } 411 | 412 | $.extend(params, { 413 | url: url, 414 | dataType: options.dataType, 415 | data: data, 416 | success: function (data) { 417 | if (requestNumber < requestSequence) { 418 | return; 419 | } 420 | // TODO - replace query.page with query so users have access to term, page, etc. 421 | var results = options.results(data, query.page); 422 | query.callback(results); 423 | } 424 | }); 425 | handler = transport.call(self, params); 426 | }, quietMillis); 427 | }; 428 | } 429 | 430 | /** 431 | * Produces a query function that works with a local array 432 | * 433 | * @param options object containing configuration parameters. The options parameter can either be an array or an 434 | * object. 435 | * 436 | * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys. 437 | * 438 | * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain 439 | * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text' 440 | * key can either be a String in which case it is expected that each element in the 'data' array has a key with the 441 | * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract 442 | * the text. 443 | */ 444 | function local(options) { 445 | var data = options, // data elements 446 | dataText, 447 | tmp, 448 | text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search 449 | 450 | if ($.isArray(data)) { 451 | tmp = data; 452 | data = { results: tmp }; 453 | } 454 | 455 | if ($.isFunction(data) === false) { 456 | tmp = data; 457 | data = function() { return tmp; }; 458 | } 459 | 460 | var dataItem = data(); 461 | if (dataItem.text) { 462 | text = dataItem.text; 463 | // if text is not a function we assume it to be a key name 464 | if (!$.isFunction(text)) { 465 | dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available 466 | text = function (item) { return item[dataText]; }; 467 | } 468 | } 469 | 470 | return function (query) { 471 | var t = query.term, filtered = { results: [] }, process; 472 | if (t === "") { 473 | query.callback(data()); 474 | return; 475 | } 476 | 477 | process = function(datum, collection) { 478 | var group, attr; 479 | datum = datum[0]; 480 | if (datum.children) { 481 | group = {}; 482 | for (attr in datum) { 483 | if (datum.hasOwnProperty(attr)) group[attr]=datum[attr]; 484 | } 485 | group.children=[]; 486 | $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); }); 487 | if (group.children.length || query.matcher(t, text(group), datum)) { 488 | collection.push(group); 489 | } 490 | } else { 491 | if (query.matcher(t, text(datum), datum)) { 492 | collection.push(datum); 493 | } 494 | } 495 | }; 496 | 497 | $(data().results).each2(function(i, datum) { process(datum, filtered.results); }); 498 | query.callback(filtered); 499 | }; 500 | } 501 | 502 | // TODO javadoc 503 | function tags(data) { 504 | var isFunc = $.isFunction(data); 505 | return function (query) { 506 | var t = query.term, filtered = {results: []}; 507 | $(isFunc ? data() : data).each(function () { 508 | var isObject = this.text !== undefined, 509 | text = isObject ? this.text : this; 510 | if (t === "" || query.matcher(t, text)) { 511 | filtered.results.push(isObject ? this : {id: this, text: this}); 512 | } 513 | }); 514 | query.callback(filtered); 515 | }; 516 | } 517 | 518 | /** 519 | * Checks if the formatter function should be used. 520 | * 521 | * Throws an error if it is not a function. Returns true if it should be used, 522 | * false if no formatting should be performed. 523 | * 524 | * @param formatter 525 | */ 526 | function checkFormatter(formatter, formatterName) { 527 | if ($.isFunction(formatter)) return true; 528 | if (!formatter) return false; 529 | throw new Error("formatterName must be a function or a falsy value"); 530 | } 531 | 532 | function evaluate(val) { 533 | return $.isFunction(val) ? val() : val; 534 | } 535 | 536 | function countResults(results) { 537 | var count = 0; 538 | $.each(results, function(i, item) { 539 | if (item.children) { 540 | count += countResults(item.children); 541 | } else { 542 | count++; 543 | } 544 | }); 545 | return count; 546 | } 547 | 548 | /** 549 | * Default tokenizer. This function uses breaks the input on substring match of any string from the 550 | * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those 551 | * two options have to be defined in order for the tokenizer to work. 552 | * 553 | * @param input text user has typed so far or pasted into the search field 554 | * @param selection currently selected choices 555 | * @param selectCallback function(choice) callback tho add the choice to selection 556 | * @param opts select2's opts 557 | * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value 558 | */ 559 | function defaultTokenizer(input, selection, selectCallback, opts) { 560 | var original = input, // store the original so we can compare and know if we need to tell the search to update its text 561 | dupe = false, // check for whether a token we extracted represents a duplicate selected choice 562 | token, // token 563 | index, // position at which the separator was found 564 | i, l, // looping variables 565 | separator; // the matched separator 566 | 567 | if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined; 568 | 569 | while (true) { 570 | index = -1; 571 | 572 | for (i = 0, l = opts.tokenSeparators.length; i < l; i++) { 573 | separator = opts.tokenSeparators[i]; 574 | index = input.indexOf(separator); 575 | if (index >= 0) break; 576 | } 577 | 578 | if (index < 0) break; // did not find any token separator in the input string, bail 579 | 580 | token = input.substring(0, index); 581 | input = input.substring(index + separator.length); 582 | 583 | if (token.length > 0) { 584 | token = opts.createSearchChoice(token, selection); 585 | if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) { 586 | dupe = false; 587 | for (i = 0, l = selection.length; i < l; i++) { 588 | if (equal(opts.id(token), opts.id(selection[i]))) { 589 | dupe = true; break; 590 | } 591 | } 592 | 593 | if (!dupe) selectCallback(token); 594 | } 595 | } 596 | } 597 | 598 | if (original!==input) return input; 599 | } 600 | 601 | /** 602 | * Creates a new class 603 | * 604 | * @param superClass 605 | * @param methods 606 | */ 607 | function clazz(SuperClass, methods) { 608 | var constructor = function () {}; 609 | constructor.prototype = new SuperClass; 610 | constructor.prototype.constructor = constructor; 611 | constructor.prototype.parent = SuperClass.prototype; 612 | constructor.prototype = $.extend(constructor.prototype, methods); 613 | return constructor; 614 | } 615 | 616 | AbstractSelect2 = clazz(Object, { 617 | 618 | // abstract 619 | bind: function (func) { 620 | var self = this; 621 | return function () { 622 | func.apply(self, arguments); 623 | }; 624 | }, 625 | 626 | // abstract 627 | init: function (opts) { 628 | var results, search, resultsSelector = ".select2-results", disabled, readonly; 629 | 630 | // prepare options 631 | this.opts = opts = this.prepareOpts(opts); 632 | 633 | this.id=opts.id; 634 | 635 | // destroy if called on an existing component 636 | if (opts.element.data("select2") !== undefined && 637 | opts.element.data("select2") !== null) { 638 | this.destroy(); 639 | } 640 | 641 | this.container = this.createContainer(); 642 | 643 | this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()); 644 | this.containerSelector="#"+this.containerId.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1'); 645 | this.container.attr("id", this.containerId); 646 | 647 | // cache the body so future lookups are cheap 648 | this.body = thunk(function() { return opts.element.closest("body"); }); 649 | 650 | syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass); 651 | 652 | this.container.css(evaluate(opts.containerCss)); 653 | this.container.addClass(evaluate(opts.containerCssClass)); 654 | 655 | this.elementTabIndex = this.opts.element.attr("tabindex"); 656 | 657 | // swap container for the element 658 | this.opts.element 659 | .data("select2", this) 660 | .attr("tabindex", "-1") 661 | .before(this.container); 662 | this.container.data("select2", this); 663 | 664 | this.dropdown = this.container.find(".select2-drop"); 665 | this.dropdown.addClass(evaluate(opts.dropdownCssClass)); 666 | this.dropdown.data("select2", this); 667 | 668 | this.results = results = this.container.find(resultsSelector); 669 | this.search = search = this.container.find("input.select2-input"); 670 | 671 | this.resultsPage = 0; 672 | this.context = null; 673 | 674 | // initialize the container 675 | this.initContainer(); 676 | 677 | installFilteredMouseMove(this.results); 678 | this.dropdown.on("mousemove-filtered touchstart touchmove touchend", resultsSelector, this.bind(this.highlightUnderEvent)); 679 | 680 | installDebouncedScroll(80, this.results); 681 | this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded)); 682 | 683 | // do not propagate change event from the search field out of the component 684 | $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();}); 685 | $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();}); 686 | 687 | // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel 688 | if ($.fn.mousewheel) { 689 | results.mousewheel(function (e, delta, deltaX, deltaY) { 690 | var top = results.scrollTop(), height; 691 | if (deltaY > 0 && top - deltaY <= 0) { 692 | results.scrollTop(0); 693 | killEvent(e); 694 | } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) { 695 | results.scrollTop(results.get(0).scrollHeight - results.height()); 696 | killEvent(e); 697 | } 698 | }); 699 | } 700 | 701 | installKeyUpChangeEvent(search); 702 | search.on("keyup-change input paste", this.bind(this.updateResults)); 703 | search.on("focus", function () { search.addClass("select2-focused"); }); 704 | search.on("blur", function () { search.removeClass("select2-focused");}); 705 | 706 | this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) { 707 | if ($(e.target).closest(".select2-result-selectable").length > 0) { 708 | this.highlightUnderEvent(e); 709 | this.selectHighlighted(e); 710 | } 711 | })); 712 | 713 | // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening 714 | // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's 715 | // dom it will trigger the popup close, which is not what we want 716 | this.dropdown.on("click mouseup mousedown", function (e) { e.stopPropagation(); }); 717 | 718 | if ($.isFunction(this.opts.initSelection)) { 719 | // initialize selection based on the current value of the source element 720 | this.initSelection(); 721 | 722 | // if the user has provided a function that can set selection based on the value of the source element 723 | // we monitor the change event on the element and trigger it, allowing for two way synchronization 724 | this.monitorSource(); 725 | } 726 | 727 | if (opts.maximumInputLength !== null) { 728 | this.search.attr("maxlength", opts.maximumInputLength); 729 | } 730 | 731 | var disabled = opts.element.prop("disabled"); 732 | if (disabled === undefined) disabled = false; 733 | this.enable(!disabled); 734 | 735 | var readonly = opts.element.prop("readonly"); 736 | if (readonly === undefined) readonly = false; 737 | this.readonly(readonly); 738 | 739 | // Calculate size of scrollbar 740 | scrollBarDimensions = scrollBarDimensions || measureScrollbar(); 741 | 742 | this.autofocus = opts.element.prop("autofocus") 743 | opts.element.prop("autofocus", false); 744 | if (this.autofocus) this.focus(); 745 | }, 746 | 747 | // abstract 748 | destroy: function () { 749 | var select2 = this.opts.element.data("select2"); 750 | 751 | if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; } 752 | 753 | if (select2 !== undefined) { 754 | 755 | select2.container.remove(); 756 | select2.dropdown.remove(); 757 | select2.opts.element 758 | .removeClass("select2-offscreen") 759 | .removeData("select2") 760 | .off(".select2") 761 | .attr({"tabindex": this.elementTabIndex}) 762 | .prop("autofocus", this.autofocus||false) 763 | .show(); 764 | } 765 | }, 766 | 767 | // abstract 768 | optionToData: function(element) { 769 | if (element.is("option")) { 770 | return { 771 | id:element.prop("value"), 772 | text:element.text(), 773 | element: element.get(), 774 | css: element.attr("class"), 775 | disabled: element.prop("disabled"), 776 | locked: equal(element.attr("locked"), "locked") 777 | }; 778 | } else if (element.is("optgroup")) { 779 | return { 780 | text:element.attr("label"), 781 | children:[], 782 | element: element.get(), 783 | css: element.attr("class") 784 | }; 785 | } 786 | }, 787 | 788 | // abstract 789 | prepareOpts: function (opts) { 790 | var element, select, idKey, ajaxUrl, self = this; 791 | 792 | element = opts.element; 793 | 794 | if (element.get(0).tagName.toLowerCase() === "select") { 795 | this.select = select = opts.element; 796 | } 797 | 798 | if (select) { 799 | // these options are not allowed when attached to a select because they are picked up off the element itself 800 | $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () { 801 | if (this in opts) { 802 | throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a ", 1703 | "
" , 1704 | " " , 1707 | "
    " , 1708 | "
" , 1709 | "
"].join("")); 1710 | return container; 1711 | }, 1712 | 1713 | // single 1714 | enableInterface: function() { 1715 | if (this.parent.enableInterface.apply(this, arguments)) { 1716 | this.focusser.prop("disabled", !this.isInterfaceEnabled()); 1717 | } 1718 | }, 1719 | 1720 | // single 1721 | opening: function () { 1722 | var el, range; 1723 | this.parent.opening.apply(this, arguments); 1724 | if (this.showSearchInput !== false) { 1725 | // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range 1726 | // all other browsers handle this just fine 1727 | 1728 | this.search.val(this.focusser.val()); 1729 | } 1730 | this.search.focus(); 1731 | // in IE we have to move the cursor to the end after focussing, otherwise it will be at the beginning and 1732 | // new text will appear *before* focusser.val() 1733 | el = this.search.get(0); 1734 | if (el.createTextRange) { 1735 | range = el.createTextRange(); 1736 | range.collapse(false); 1737 | range.select(); 1738 | } 1739 | 1740 | this.focusser.prop("disabled", true).val(""); 1741 | this.updateResults(true); 1742 | this.opts.element.trigger($.Event("select2-open")); 1743 | }, 1744 | 1745 | // single 1746 | close: function () { 1747 | if (!this.opened()) return; 1748 | this.parent.close.apply(this, arguments); 1749 | this.focusser.removeAttr("disabled"); 1750 | this.focusser.focus(); 1751 | }, 1752 | 1753 | // single 1754 | focus: function () { 1755 | if (this.opened()) { 1756 | this.close(); 1757 | } else { 1758 | this.focusser.removeAttr("disabled"); 1759 | this.focusser.focus(); 1760 | } 1761 | }, 1762 | 1763 | // single 1764 | isFocused: function () { 1765 | return this.container.hasClass("select2-container-active"); 1766 | }, 1767 | 1768 | // single 1769 | cancel: function () { 1770 | this.parent.cancel.apply(this, arguments); 1771 | this.focusser.removeAttr("disabled"); 1772 | this.focusser.focus(); 1773 | }, 1774 | 1775 | // single 1776 | initContainer: function () { 1777 | 1778 | var selection, 1779 | container = this.container, 1780 | dropdown = this.dropdown; 1781 | 1782 | this.showSearch(false); 1783 | 1784 | this.selection = selection = container.find(".select2-choice"); 1785 | 1786 | this.focusser = container.find(".select2-focusser"); 1787 | 1788 | // rewrite labels from original element to focusser 1789 | this.focusser.attr("id", "s2id_autogen"+nextUid()); 1790 | 1791 | $("label[for='" + this.opts.element.attr("id") + "']") 1792 | .attr('for', this.focusser.attr('id')); 1793 | 1794 | this.focusser.attr("tabindex", this.elementTabIndex); 1795 | 1796 | this.search.on("keydown", this.bind(function (e) { 1797 | if (!this.isInterfaceEnabled()) return; 1798 | 1799 | if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { 1800 | // prevent the page from scrolling 1801 | killEvent(e); 1802 | return; 1803 | } 1804 | 1805 | switch (e.which) { 1806 | case KEY.UP: 1807 | case KEY.DOWN: 1808 | this.moveHighlight((e.which === KEY.UP) ? -1 : 1); 1809 | killEvent(e); 1810 | return; 1811 | case KEY.ENTER: 1812 | this.selectHighlighted(); 1813 | killEvent(e); 1814 | return; 1815 | case KEY.TAB: 1816 | this.selectHighlighted({noFocus: true}); 1817 | return; 1818 | case KEY.ESC: 1819 | this.cancel(e); 1820 | killEvent(e); 1821 | return; 1822 | } 1823 | })); 1824 | 1825 | this.search.on("blur", this.bind(function(e) { 1826 | // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown. 1827 | // without this the search field loses focus which is annoying 1828 | if (document.activeElement === this.body().get(0)) { 1829 | window.setTimeout(this.bind(function() { 1830 | this.search.focus(); 1831 | }), 0); 1832 | } 1833 | })); 1834 | 1835 | this.focusser.on("keydown", this.bind(function (e) { 1836 | if (!this.isInterfaceEnabled()) return; 1837 | 1838 | if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) { 1839 | return; 1840 | } 1841 | 1842 | if (this.opts.openOnEnter === false && e.which === KEY.ENTER) { 1843 | killEvent(e); 1844 | return; 1845 | } 1846 | 1847 | if (e.which == KEY.DOWN || e.which == KEY.UP 1848 | || (e.which == KEY.ENTER && this.opts.openOnEnter)) { 1849 | this.open(); 1850 | killEvent(e); 1851 | return; 1852 | } 1853 | 1854 | if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) { 1855 | if (this.opts.allowClear) { 1856 | this.clear(); 1857 | } 1858 | killEvent(e); 1859 | return; 1860 | } 1861 | })); 1862 | 1863 | 1864 | installKeyUpChangeEvent(this.focusser); 1865 | this.focusser.on("keyup-change input", this.bind(function(e) { 1866 | e.stopPropagation(); 1867 | if (this.opened()) return; 1868 | this.open(); 1869 | })); 1870 | 1871 | selection.on("mousedown", "abbr", this.bind(function (e) { 1872 | if (!this.isInterfaceEnabled()) return; 1873 | this.clear(); 1874 | killEventImmediately(e); 1875 | this.close(); 1876 | this.selection.focus(); 1877 | })); 1878 | 1879 | selection.on("mousedown", this.bind(function (e) { 1880 | 1881 | if (!this.container.hasClass("select2-container-active")) { 1882 | this.opts.element.trigger($.Event("select2-focus")); 1883 | } 1884 | 1885 | if (this.opened()) { 1886 | this.close(); 1887 | } else if (this.isInterfaceEnabled()) { 1888 | this.open(); 1889 | } 1890 | 1891 | killEvent(e); 1892 | })); 1893 | 1894 | dropdown.on("mousedown", this.bind(function() { this.search.focus(); })); 1895 | 1896 | selection.on("focus", this.bind(function(e) { 1897 | killEvent(e); 1898 | })); 1899 | 1900 | this.focusser.on("focus", this.bind(function(){ 1901 | if (!this.container.hasClass("select2-container-active")) { 1902 | this.opts.element.trigger($.Event("select2-focus")); 1903 | } 1904 | this.container.addClass("select2-container-active"); 1905 | })).on("blur", this.bind(function() { 1906 | if (!this.opened()) { 1907 | this.container.removeClass("select2-container-active"); 1908 | this.opts.element.trigger($.Event("select2-blur")); 1909 | } 1910 | })); 1911 | this.search.on("focus", this.bind(function(){ 1912 | if (!this.container.hasClass("select2-container-active")) { 1913 | this.opts.element.trigger($.Event("select2-focus")); 1914 | } 1915 | this.container.addClass("select2-container-active"); 1916 | })); 1917 | 1918 | this.initContainerWidth(); 1919 | this.opts.element.addClass("select2-offscreen"); 1920 | this.setPlaceholder(); 1921 | 1922 | }, 1923 | 1924 | // single 1925 | clear: function(triggerChange) { 1926 | var data=this.selection.data("select2-data"); 1927 | if (data) { // guard against queued quick consecutive clicks 1928 | this.opts.element.val(""); 1929 | this.selection.find("span").empty(); 1930 | this.selection.removeData("select2-data"); 1931 | this.setPlaceholder(); 1932 | 1933 | if (triggerChange !== false){ 1934 | this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data }); 1935 | this.triggerChange({removed:data}); 1936 | } 1937 | } 1938 | }, 1939 | 1940 | /** 1941 | * Sets selection based on source element's value 1942 | */ 1943 | // single 1944 | initSelection: function () { 1945 | var selected; 1946 | if (this.opts.element.val() === "" && this.opts.element.text() === "") { 1947 | this.updateSelection([]); 1948 | this.close(); 1949 | this.setPlaceholder(); 1950 | } else { 1951 | var self = this; 1952 | this.opts.initSelection.call(null, this.opts.element, function(selected){ 1953 | if (selected !== undefined && selected !== null) { 1954 | self.updateSelection(selected); 1955 | self.close(); 1956 | self.setPlaceholder(); 1957 | } 1958 | }); 1959 | } 1960 | }, 1961 | 1962 | // single 1963 | prepareOpts: function () { 1964 | var opts = this.parent.prepareOpts.apply(this, arguments), 1965 | self=this; 1966 | 1967 | if (opts.element.get(0).tagName.toLowerCase() === "select") { 1968 | // install the selection initializer 1969 | opts.initSelection = function (element, callback) { 1970 | var selected = element.find(":selected"); 1971 | // a single select box always has a value, no need to null check 'selected' 1972 | callback(self.optionToData(selected)); 1973 | }; 1974 | } else if ("data" in opts) { 1975 | // install default initSelection when applied to hidden input and data is local 1976 | opts.initSelection = opts.initSelection || function (element, callback) { 1977 | var id = element.val(); 1978 | //search in data by id, storing the actual matching item 1979 | var match = null; 1980 | opts.query({ 1981 | matcher: function(term, text, el){ 1982 | var is_match = equal(id, opts.id(el)); 1983 | if (is_match) { 1984 | match = el; 1985 | } 1986 | return is_match; 1987 | }, 1988 | callback: !$.isFunction(callback) ? $.noop : function() { 1989 | callback(match); 1990 | } 1991 | }); 1992 | }; 1993 | } 1994 | 1995 | return opts; 1996 | }, 1997 | 1998 | // single 1999 | getPlaceholder: function() { 2000 | // if a placeholder is specified on a single select without the first empty option ignore it 2001 | if (this.select) { 2002 | if (this.select.find("option").first().text() !== "") { 2003 | return undefined; 2004 | } 2005 | } 2006 | 2007 | return this.parent.getPlaceholder.apply(this, arguments); 2008 | }, 2009 | 2010 | // single 2011 | setPlaceholder: function () { 2012 | var placeholder = this.getPlaceholder(); 2013 | 2014 | if (this.opts.element.val() === "" && placeholder !== undefined) { 2015 | 2016 | // check for a first blank option if attached to a select 2017 | if (this.select && this.select.find("option:first").text() !== "") return; 2018 | 2019 | this.selection.find("span").html(this.opts.escapeMarkup(placeholder)); 2020 | 2021 | this.selection.addClass("select2-default"); 2022 | 2023 | this.container.removeClass("select2-allowclear"); 2024 | } 2025 | }, 2026 | 2027 | // single 2028 | postprocessResults: function (data, initial, noHighlightUpdate) { 2029 | var selected = 0, self = this, showSearchInput = true; 2030 | 2031 | // find the selected element in the result list 2032 | 2033 | this.findHighlightableChoices().each2(function (i, elm) { 2034 | if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) { 2035 | selected = i; 2036 | return false; 2037 | } 2038 | }); 2039 | 2040 | // and highlight it 2041 | if (noHighlightUpdate !== false) { 2042 | this.highlight(selected); 2043 | } 2044 | 2045 | // show the search box if this is the first we got the results and there are enough of them for search 2046 | 2047 | if (initial === true && this.showSearchInput === false) { 2048 | var min=this.opts.minimumResultsForSearch; 2049 | if (min>=0) { 2050 | this.showSearch(countResults(data.results)>=min); 2051 | } 2052 | } 2053 | 2054 | }, 2055 | 2056 | // single 2057 | showSearch: function(showSearchInput) { 2058 | this.showSearchInput = showSearchInput; 2059 | 2060 | this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput); 2061 | this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput); 2062 | //add "select2-with-searchbox" to the container if search box is shown 2063 | $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput); 2064 | }, 2065 | 2066 | // single 2067 | onSelect: function (data, options) { 2068 | 2069 | if (!this.triggerSelect(data)) { return; } 2070 | 2071 | var old = this.opts.element.val(), 2072 | oldData = this.data(); 2073 | 2074 | this.opts.element.val(this.id(data)); 2075 | this.updateSelection(data); 2076 | 2077 | this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data }); 2078 | 2079 | this.close(); 2080 | 2081 | if (!options || !options.noFocus) 2082 | this.selection.focus(); 2083 | 2084 | if (!equal(old, this.id(data))) { this.triggerChange({added:data,removed:oldData}); } 2085 | }, 2086 | 2087 | // single 2088 | updateSelection: function (data) { 2089 | 2090 | var container=this.selection.find("span"), formatted; 2091 | 2092 | this.selection.data("select2-data", data); 2093 | 2094 | container.empty(); 2095 | formatted=this.opts.formatSelection(data, container); 2096 | if (formatted !== undefined) { 2097 | container.append(this.opts.escapeMarkup(formatted)); 2098 | } 2099 | 2100 | this.selection.removeClass("select2-default"); 2101 | 2102 | if (this.opts.allowClear && this.getPlaceholder() !== undefined) { 2103 | this.container.addClass("select2-allowclear"); 2104 | } 2105 | }, 2106 | 2107 | // single 2108 | val: function () { 2109 | var val, 2110 | triggerChange = false, 2111 | data = null, 2112 | self = this, 2113 | oldData = this.data(); 2114 | 2115 | if (arguments.length === 0) { 2116 | return this.opts.element.val(); 2117 | } 2118 | 2119 | val = arguments[0]; 2120 | 2121 | if (arguments.length > 1) { 2122 | triggerChange = arguments[1]; 2123 | } 2124 | 2125 | if (this.select) { 2126 | this.select 2127 | .val(val) 2128 | .find(":selected").each2(function (i, elm) { 2129 | data = self.optionToData(elm); 2130 | return false; 2131 | }); 2132 | this.updateSelection(data); 2133 | this.setPlaceholder(); 2134 | if (triggerChange) { 2135 | this.triggerChange({added: data, removed:oldData}); 2136 | } 2137 | } else { 2138 | if (this.opts.initSelection === undefined) { 2139 | throw new Error("cannot call val() if initSelection() is not defined"); 2140 | } 2141 | // val is an id. !val is true for [undefined,null,'',0] - 0 is legal 2142 | if (!val && val !== 0) { 2143 | this.clear(triggerChange); 2144 | return; 2145 | } 2146 | this.opts.element.val(val); 2147 | this.opts.initSelection(this.opts.element, function(data){ 2148 | self.opts.element.val(!data ? "" : self.id(data)); 2149 | self.updateSelection(data); 2150 | self.setPlaceholder(); 2151 | if (triggerChange) { 2152 | self.triggerChange({added: data, removed:oldData}); 2153 | } 2154 | }); 2155 | } 2156 | }, 2157 | 2158 | // single 2159 | clearSearch: function () { 2160 | this.search.val(""); 2161 | this.focusser.val(""); 2162 | }, 2163 | 2164 | // single 2165 | data: function(value, triggerChange) { 2166 | var data; 2167 | 2168 | if (arguments.length === 0) { 2169 | data = this.selection.data("select2-data"); 2170 | if (data == undefined) data = null; 2171 | return data; 2172 | } else { 2173 | if (!value || value === "") { 2174 | this.clear(triggerChange); 2175 | } else { 2176 | data = this.data(); 2177 | this.opts.element.val(!value ? "" : this.id(value)); 2178 | this.updateSelection(value); 2179 | if (triggerChange) { 2180 | this.triggerChange({added: value, removed:data}); 2181 | } 2182 | } 2183 | } 2184 | } 2185 | }); 2186 | 2187 | MultiSelect2 = clazz(AbstractSelect2, { 2188 | 2189 | // multi 2190 | createContainer: function () { 2191 | var container = $(document.createElement("div")).attr({ 2192 | "class": "select2-container select2-container-multi" 2193 | }).html([ 2194 | "
    ", 2195 | //"
  • California
  • " , 2196 | "
  • " , 2197 | " " , 2198 | "
  • " , 2199 | "
" , 2200 | "
" , 2201 | "
    " , 2202 | "
" , 2203 | "
"].join("")); 2204 | return container; 2205 | }, 2206 | 2207 | // multi 2208 | prepareOpts: function () { 2209 | var opts = this.parent.prepareOpts.apply(this, arguments), 2210 | self=this; 2211 | 2212 | // TODO validate placeholder is a string if specified 2213 | 2214 | if (opts.element.get(0).tagName.toLowerCase() === "select") { 2215 | // install sthe selection initializer 2216 | opts.initSelection = function (element, callback) { 2217 | 2218 | var data = []; 2219 | 2220 | element.find(":selected").each2(function (i, elm) { 2221 | data.push(self.optionToData(elm)); 2222 | }); 2223 | callback(data); 2224 | }; 2225 | } else if ("data" in opts) { 2226 | // install default initSelection when applied to hidden input and data is local 2227 | opts.initSelection = opts.initSelection || function (element, callback) { 2228 | var ids = splitVal(element.val(), opts.separator); 2229 | //search in data by array of ids, storing matching items in a list 2230 | var matches = []; 2231 | opts.query({ 2232 | matcher: function(term, text, el){ 2233 | var is_match = $.grep(ids, function(id) { 2234 | return equal(id, opts.id(el)); 2235 | }).length; 2236 | if (is_match) { 2237 | matches.push(el); 2238 | } 2239 | return is_match; 2240 | }, 2241 | callback: !$.isFunction(callback) ? $.noop : function() { 2242 | // reorder matches based on the order they appear in the ids array because right now 2243 | // they are in the order in which they appear in data array 2244 | var ordered = []; 2245 | for (var i = 0; i < ids.length; i++) { 2246 | var id = ids[i]; 2247 | for (var j = 0; j < matches.length; j++) { 2248 | var match = matches[j]; 2249 | if (equal(id, opts.id(match))) { 2250 | ordered.push(match); 2251 | matches.splice(j, 1); 2252 | break; 2253 | } 2254 | } 2255 | } 2256 | callback(ordered); 2257 | } 2258 | }); 2259 | }; 2260 | } 2261 | 2262 | return opts; 2263 | }, 2264 | 2265 | selectChoice: function (choice) { 2266 | 2267 | var selected = this.container.find(".select2-search-choice-focus"); 2268 | if (selected.length && choice && choice[0] == selected[0]) { 2269 | 2270 | } else { 2271 | if (selected.length) { 2272 | this.opts.element.trigger("choice-deselected", selected); 2273 | } 2274 | selected.removeClass("select2-search-choice-focus"); 2275 | if (choice && choice.length) { 2276 | this.close(); 2277 | choice.addClass("select2-search-choice-focus"); 2278 | this.opts.element.trigger("choice-selected", choice); 2279 | } 2280 | } 2281 | }, 2282 | 2283 | // multi 2284 | initContainer: function () { 2285 | 2286 | var selector = ".select2-choices", selection; 2287 | 2288 | this.searchContainer = this.container.find(".select2-search-field"); 2289 | this.selection = selection = this.container.find(selector); 2290 | 2291 | var _this = this; 2292 | this.selection.on("mousedown", ".select2-search-choice", function (e) { 2293 | //killEvent(e); 2294 | _this.search[0].focus(); 2295 | _this.selectChoice($(this)); 2296 | }) 2297 | //.sortable({ 2298 | // items: " > li", 2299 | // tolerance: "pointer", 2300 | // revert: 100 2301 | //}); 2302 | 2303 | // rewrite labels from original element to focusser 2304 | this.search.attr("id", "s2id_autogen"+nextUid()); 2305 | $("label[for='" + this.opts.element.attr("id") + "']") 2306 | .attr('for', this.search.attr('id')); 2307 | 2308 | this.search.on("input paste", this.bind(function() { 2309 | if (!this.isInterfaceEnabled()) return; 2310 | if (!this.opened()) { 2311 | this.open(); 2312 | } 2313 | })); 2314 | 2315 | this.search.attr("tabindex", this.elementTabIndex); 2316 | 2317 | this.keydowns = 0; 2318 | this.search.on("keydown", this.bind(function (e) { 2319 | if (!this.isInterfaceEnabled()) return; 2320 | 2321 | ++this.keydowns; 2322 | var selected = selection.find(".select2-search-choice-focus"); 2323 | var prev = selected.prev(".select2-search-choice:not(.select2-locked)"); 2324 | var next = selected.next(".select2-search-choice:not(.select2-locked)"); 2325 | var pos = getCursorInfo(this.search); 2326 | 2327 | if (selected.length && 2328 | (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) { 2329 | var selectedChoice = selected; 2330 | if (e.which == KEY.LEFT && prev.length) { 2331 | selectedChoice = prev; 2332 | } 2333 | else if (e.which == KEY.RIGHT) { 2334 | selectedChoice = next.length ? next : null; 2335 | } 2336 | else if (e.which === KEY.BACKSPACE) { 2337 | this.unselect(selected.first()); 2338 | this.search.width(10); 2339 | selectedChoice = prev.length ? prev : next; 2340 | } else if (e.which == KEY.DELETE) { 2341 | this.unselect(selected.first()); 2342 | this.search.width(10); 2343 | selectedChoice = next.length ? next : null; 2344 | } else if (e.which == KEY.ENTER) { 2345 | selectedChoice = null; 2346 | } 2347 | 2348 | this.selectChoice(selectedChoice); 2349 | killEvent(e); 2350 | if (!selectedChoice || !selectedChoice.length) { 2351 | this.open(); 2352 | } 2353 | return; 2354 | } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1) 2355 | || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) { 2356 | 2357 | this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last()); 2358 | killEvent(e); 2359 | return; 2360 | } else { 2361 | this.selectChoice(null); 2362 | } 2363 | 2364 | if (this.opened()) { 2365 | switch (e.which) { 2366 | case KEY.UP: 2367 | case KEY.DOWN: 2368 | this.moveHighlight((e.which === KEY.UP) ? -1 : 1); 2369 | killEvent(e); 2370 | return; 2371 | case KEY.ENTER: 2372 | this.selectHighlighted(); 2373 | killEvent(e); 2374 | return; 2375 | case KEY.TAB: 2376 | this.selectHighlighted({noFocus:true}); 2377 | return; 2378 | case KEY.ESC: 2379 | this.cancel(e); 2380 | killEvent(e); 2381 | return; 2382 | } 2383 | } 2384 | 2385 | if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) 2386 | || e.which === KEY.BACKSPACE || e.which === KEY.ESC) { 2387 | return; 2388 | } 2389 | 2390 | if (e.which === KEY.ENTER) { 2391 | if (this.opts.openOnEnter === false) { 2392 | return; 2393 | } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) { 2394 | return; 2395 | } 2396 | } 2397 | 2398 | this.open(); 2399 | 2400 | if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) { 2401 | // prevent the page from scrolling 2402 | killEvent(e); 2403 | } 2404 | 2405 | if (e.which === KEY.ENTER) { 2406 | // prevent form from being submitted 2407 | killEvent(e); 2408 | } 2409 | 2410 | })); 2411 | 2412 | this.search.on("keyup", this.bind(function (e) { 2413 | this.keydowns = 0; 2414 | this.resizeSearch(); 2415 | }) 2416 | ); 2417 | 2418 | this.search.on("blur", this.bind(function(e) { 2419 | this.container.removeClass("select2-container-active"); 2420 | this.search.removeClass("select2-focused"); 2421 | this.selectChoice(null); 2422 | if (!this.opened()) this.clearSearch(); 2423 | e.stopImmediatePropagation(); 2424 | this.opts.element.trigger($.Event("select2-blur")); 2425 | })); 2426 | 2427 | this.container.on("mousedown", selector, this.bind(function (e) { 2428 | if (!this.isInterfaceEnabled()) return; 2429 | if ($(e.target).closest(".select2-search-choice").length > 0) { 2430 | // clicked inside a select2 search choice, do not open 2431 | return; 2432 | } 2433 | this.selectChoice(null); 2434 | this.clearPlaceholder(); 2435 | if (!this.container.hasClass("select2-container-active")) { 2436 | this.opts.element.trigger($.Event("select2-focus")); 2437 | } 2438 | this.open(); 2439 | this.focusSearch(); 2440 | e.preventDefault(); 2441 | })); 2442 | 2443 | this.container.on("focus", selector, this.bind(function () { 2444 | if (!this.isInterfaceEnabled()) return; 2445 | if (!this.container.hasClass("select2-container-active")) { 2446 | this.opts.element.trigger($.Event("select2-focus")); 2447 | } 2448 | this.container.addClass("select2-container-active"); 2449 | this.dropdown.addClass("select2-drop-active"); 2450 | this.clearPlaceholder(); 2451 | })); 2452 | 2453 | this.initContainerWidth(); 2454 | this.opts.element.addClass("select2-offscreen"); 2455 | 2456 | // set the placeholder if necessary 2457 | this.clearSearch(); 2458 | }, 2459 | 2460 | // multi 2461 | enableInterface: function() { 2462 | if (this.parent.enableInterface.apply(this, arguments)) { 2463 | this.search.prop("disabled", !this.isInterfaceEnabled()); 2464 | } 2465 | }, 2466 | 2467 | // multi 2468 | initSelection: function () { 2469 | var data; 2470 | if (this.opts.element.val() === "" && this.opts.element.text() === "") { 2471 | this.updateSelection([]); 2472 | this.close(); 2473 | // set the placeholder if necessary 2474 | this.clearSearch(); 2475 | } 2476 | if (this.select || this.opts.element.val() !== "") { 2477 | var self = this; 2478 | this.opts.initSelection.call(null, this.opts.element, function(data){ 2479 | if (data !== undefined && data !== null) { 2480 | self.updateSelection(data); 2481 | self.close(); 2482 | // set the placeholder if necessary 2483 | self.clearSearch(); 2484 | } 2485 | }); 2486 | } 2487 | }, 2488 | 2489 | // multi 2490 | clearSearch: function () { 2491 | var placeholder = this.getPlaceholder(), 2492 | maxWidth = this.getMaxSearchWidth(); 2493 | 2494 | if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) { 2495 | this.search.val(placeholder).addClass("select2-default"); 2496 | // stretch the search box to full width of the container so as much of the placeholder is visible as possible 2497 | // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944 2498 | this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width")); 2499 | } else { 2500 | this.search.val("").width(10); 2501 | } 2502 | }, 2503 | 2504 | // multi 2505 | clearPlaceholder: function () { 2506 | if (this.search.hasClass("select2-default")) { 2507 | this.search.val("").removeClass("select2-default"); 2508 | } 2509 | }, 2510 | 2511 | // multi 2512 | opening: function () { 2513 | this.clearPlaceholder(); // should be done before super so placeholder is not used to search 2514 | this.resizeSearch(); 2515 | 2516 | this.parent.opening.apply(this, arguments); 2517 | 2518 | this.focusSearch(); 2519 | 2520 | this.updateResults(true); 2521 | this.search.focus(); 2522 | this.opts.element.trigger($.Event("select2-open")); 2523 | }, 2524 | 2525 | // multi 2526 | close: function () { 2527 | if (!this.opened()) return; 2528 | this.parent.close.apply(this, arguments); 2529 | }, 2530 | 2531 | // multi 2532 | focus: function () { 2533 | this.close(); 2534 | this.search.focus(); 2535 | //this.opts.element.triggerHandler("focus"); 2536 | }, 2537 | 2538 | // multi 2539 | isFocused: function () { 2540 | return this.search.hasClass("select2-focused"); 2541 | }, 2542 | 2543 | // multi 2544 | updateSelection: function (data) { 2545 | var ids = [], filtered = [], self = this; 2546 | 2547 | // filter out duplicates 2548 | $(data).each(function () { 2549 | if (indexOf(self.id(this), ids) < 0) { 2550 | ids.push(self.id(this)); 2551 | filtered.push(this); 2552 | } 2553 | }); 2554 | data = filtered; 2555 | 2556 | this.selection.find(".select2-search-choice").remove(); 2557 | $(data).each(function () { 2558 | self.addSelectedChoice(this); 2559 | }); 2560 | self.postprocessResults(); 2561 | }, 2562 | 2563 | // multi 2564 | tokenize: function() { 2565 | var input = this.search.val(); 2566 | input = this.opts.tokenizer(input, this.data(), this.bind(this.onSelect), this.opts); 2567 | if (input != null && input != undefined) { 2568 | this.search.val(input); 2569 | if (input.length > 0) { 2570 | this.open(); 2571 | } 2572 | } 2573 | 2574 | }, 2575 | 2576 | // multi 2577 | onSelect: function (data, options) { 2578 | 2579 | if (!this.triggerSelect(data)) { return; } 2580 | 2581 | this.addSelectedChoice(data); 2582 | 2583 | this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data }); 2584 | 2585 | if (this.select || !this.opts.closeOnSelect) this.postprocessResults(); 2586 | 2587 | if (this.opts.closeOnSelect) { 2588 | this.close(); 2589 | this.search.width(10); 2590 | } else { 2591 | if (this.countSelectableResults()>0) { 2592 | this.search.width(10); 2593 | this.resizeSearch(); 2594 | if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) { 2595 | // if we reached max selection size repaint the results so choices 2596 | // are replaced with the max selection reached message 2597 | this.updateResults(true); 2598 | } 2599 | this.positionDropdown(); 2600 | } else { 2601 | // if nothing left to select close 2602 | this.close(); 2603 | this.search.width(10); 2604 | } 2605 | } 2606 | 2607 | // since its not possible to select an element that has already been 2608 | // added we do not need to check if this is a new element before firing change 2609 | this.triggerChange({ added: data }); 2610 | 2611 | if (!options || !options.noFocus) 2612 | this.focusSearch(); 2613 | }, 2614 | 2615 | // multi 2616 | cancel: function () { 2617 | this.close(); 2618 | this.focusSearch(); 2619 | }, 2620 | 2621 | addSelectedChoice: function (data) { 2622 | var enableChoice = !data.locked, 2623 | enabledItem = $( 2624 | "
  • " + 2625 | "
    " + 2626 | " " + 2627 | "
  • "), 2628 | disabledItem = $( 2629 | "
  • " + 2630 | "
    " + 2631 | "
  • "); 2632 | var choice = enableChoice ? enabledItem : disabledItem, 2633 | id = this.id(data), 2634 | val = this.getVal(), 2635 | formatted; 2636 | 2637 | formatted=this.opts.formatSelection(data, choice.find("div")); 2638 | if (formatted != undefined) { 2639 | choice.find("div").replaceWith("
    "+this.opts.escapeMarkup(formatted)+"
    "); 2640 | } 2641 | 2642 | if(enableChoice){ 2643 | choice.find(".select2-search-choice-close") 2644 | .on("mousedown", killEvent) 2645 | .on("click dblclick", this.bind(function (e) { 2646 | if (!this.isInterfaceEnabled()) return; 2647 | 2648 | $(e.target).closest(".select2-search-choice").fadeOut('fast', this.bind(function(){ 2649 | this.unselect($(e.target)); 2650 | this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"); 2651 | this.close(); 2652 | this.focusSearch(); 2653 | })).dequeue(); 2654 | killEvent(e); 2655 | })).on("focus", this.bind(function () { 2656 | if (!this.isInterfaceEnabled()) return; 2657 | this.container.addClass("select2-container-active"); 2658 | this.dropdown.addClass("select2-drop-active"); 2659 | })); 2660 | } 2661 | 2662 | choice.data("select2-data", data); 2663 | choice.insertBefore(this.searchContainer); 2664 | 2665 | val.push(id); 2666 | this.setVal(val); 2667 | }, 2668 | 2669 | // multi 2670 | unselect: function (selected) { 2671 | var val = this.getVal(), 2672 | data, 2673 | index; 2674 | 2675 | selected = selected.closest(".select2-search-choice"); 2676 | 2677 | if (selected.length === 0) { 2678 | throw "Invalid argument: " + selected + ". Must be .select2-search-choice"; 2679 | } 2680 | 2681 | data = selected.data("select2-data"); 2682 | 2683 | if (!data) { 2684 | // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued 2685 | // and invoked on an element already removed 2686 | return; 2687 | } 2688 | 2689 | index = indexOf(this.id(data), val); 2690 | 2691 | if (index >= 0) { 2692 | val.splice(index, 1); 2693 | this.setVal(val); 2694 | if (this.select) this.postprocessResults(); 2695 | } 2696 | selected.remove(); 2697 | 2698 | this.opts.element.trigger({ type: "removed", val: this.id(data), choice: data }); 2699 | this.triggerChange({ removed: data }); 2700 | }, 2701 | 2702 | // multi 2703 | postprocessResults: function (data, initial, noHighlightUpdate) { 2704 | var val = this.getVal(), 2705 | choices = this.results.find(".select2-result"), 2706 | compound = this.results.find(".select2-result-with-children"), 2707 | self = this; 2708 | 2709 | choices.each2(function (i, choice) { 2710 | var id = self.id(choice.data("select2-data")); 2711 | if (indexOf(id, val) >= 0) { 2712 | choice.addClass("select2-selected"); 2713 | // mark all children of the selected parent as selected 2714 | choice.find(".select2-result-selectable").addClass("select2-selected"); 2715 | } 2716 | }); 2717 | 2718 | compound.each2(function(i, choice) { 2719 | // hide an optgroup if it doesnt have any selectable children 2720 | if (!choice.is('.select2-result-selectable') 2721 | && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) { 2722 | choice.addClass("select2-selected"); 2723 | } 2724 | }); 2725 | 2726 | if (this.highlight() == -1 && noHighlightUpdate !== false){ 2727 | self.highlight(0); 2728 | } 2729 | 2730 | //If all results are chosen render formatNoMAtches 2731 | if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){ 2732 | this.results.append("
  • " + self.opts.formatNoMatches(self.search.val()) + "
  • "); 2733 | } 2734 | 2735 | }, 2736 | 2737 | // multi 2738 | getMaxSearchWidth: function() { 2739 | return this.selection.width() - getSideBorderPadding(this.search); 2740 | }, 2741 | 2742 | // multi 2743 | resizeSearch: function () { 2744 | var minimumWidth, left, maxWidth, containerLeft, searchWidth, 2745 | sideBorderPadding = getSideBorderPadding(this.search); 2746 | 2747 | minimumWidth = measureTextWidth(this.search) + 10; 2748 | 2749 | left = this.search.offset().left; 2750 | 2751 | maxWidth = this.selection.width(); 2752 | containerLeft = this.selection.offset().left; 2753 | 2754 | searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding; 2755 | 2756 | if (searchWidth < minimumWidth) { 2757 | searchWidth = maxWidth - sideBorderPadding; 2758 | } 2759 | 2760 | if (searchWidth < 40) { 2761 | searchWidth = maxWidth - sideBorderPadding; 2762 | } 2763 | 2764 | if (searchWidth <= 0) { 2765 | searchWidth = minimumWidth; 2766 | } 2767 | 2768 | this.search.width(searchWidth); 2769 | }, 2770 | 2771 | // multi 2772 | getVal: function () { 2773 | var val; 2774 | if (this.select) { 2775 | val = this.select.val(); 2776 | return val === null ? [] : val; 2777 | } else { 2778 | val = this.opts.element.val(); 2779 | return splitVal(val, this.opts.separator); 2780 | } 2781 | }, 2782 | 2783 | // multi 2784 | setVal: function (val) { 2785 | var unique; 2786 | if (this.select) { 2787 | this.select.val(val); 2788 | } else { 2789 | unique = []; 2790 | // filter out duplicates 2791 | $(val).each(function () { 2792 | if (indexOf(this, unique) < 0) unique.push(this); 2793 | }); 2794 | this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator)); 2795 | } 2796 | }, 2797 | 2798 | // multi 2799 | buildChangeDetails: function (old, current) { 2800 | var current = current.slice(0), 2801 | old = old.slice(0); 2802 | 2803 | // remove intersection from each array 2804 | for (var i = 0; i < current.length; i++) { 2805 | for (var j = 0; j < old.length; j++) { 2806 | if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) { 2807 | current.splice(i, 1); 2808 | i--; 2809 | old.splice(j, 1); 2810 | j--; 2811 | } 2812 | } 2813 | } 2814 | 2815 | return {added: current, removed: old}; 2816 | }, 2817 | 2818 | 2819 | // multi 2820 | val: function (val, triggerChange) { 2821 | var oldData, self=this, changeDetails; 2822 | 2823 | if (arguments.length === 0) { 2824 | return this.getVal(); 2825 | } 2826 | 2827 | oldData=this.data(); 2828 | if (!oldData.length) oldData=[]; 2829 | 2830 | // val is an id. !val is true for [undefined,null,'',0] - 0 is legal 2831 | if (!val && val !== 0) { 2832 | this.opts.element.val(""); 2833 | this.updateSelection([]); 2834 | this.clearSearch(); 2835 | if (triggerChange) { 2836 | this.triggerChange({added: this.data(), removed: oldData}); 2837 | } 2838 | return; 2839 | } 2840 | 2841 | // val is a list of ids 2842 | this.setVal(val); 2843 | 2844 | if (this.select) { 2845 | this.opts.initSelection(this.select, this.bind(this.updateSelection)); 2846 | if (triggerChange) { 2847 | this.triggerChange(this.buildChangeDetails(oldData, this.data())); 2848 | } 2849 | } else { 2850 | if (this.opts.initSelection === undefined) { 2851 | throw new Error("val() cannot be called if initSelection() is not defined"); 2852 | } 2853 | 2854 | this.opts.initSelection(this.opts.element, function(data){ 2855 | var ids=$(data).map(self.id); 2856 | self.setVal(ids); 2857 | self.updateSelection(data); 2858 | self.clearSearch(); 2859 | if (triggerChange) { 2860 | self.triggerChange(this.buildChangeDetails(oldData, this.data())); 2861 | } 2862 | }); 2863 | } 2864 | this.clearSearch(); 2865 | }, 2866 | 2867 | // multi 2868 | onSortStart: function() { 2869 | if (this.select) { 2870 | throw new Error("Sorting of elements is not supported when attached to instead."); 2871 | } 2872 | 2873 | // collapse search field into 0 width so its container can be collapsed as well 2874 | this.search.width(0); 2875 | // hide the container 2876 | this.searchContainer.hide(); 2877 | }, 2878 | 2879 | // multi 2880 | onSortEnd:function() { 2881 | 2882 | var val=[], self=this; 2883 | 2884 | // show search and move it to the end of the list 2885 | this.searchContainer.show(); 2886 | // make sure the search container is the last item in the list 2887 | this.searchContainer.appendTo(this.searchContainer.parent()); 2888 | // since we collapsed the width in dragStarted, we resize it here 2889 | this.resizeSearch(); 2890 | 2891 | // update selection 2892 | 2893 | this.selection.find(".select2-search-choice").each(function() { 2894 | val.push(self.opts.id($(this).data("select2-data"))); 2895 | }); 2896 | this.setVal(val); 2897 | this.triggerChange(); 2898 | }, 2899 | 2900 | // multi 2901 | data: function(values, triggerChange) { 2902 | var self=this, ids, old; 2903 | if (arguments.length === 0) { 2904 | return this.selection 2905 | .find(".select2-search-choice") 2906 | .map(function() { return $(this).data("select2-data"); }) 2907 | .get(); 2908 | } else { 2909 | old = this.data(); 2910 | if (!values) { values = []; } 2911 | ids = $.map(values, function(e) { return self.opts.id(e); }); 2912 | this.setVal(ids); 2913 | this.updateSelection(values); 2914 | this.clearSearch(); 2915 | if (triggerChange) { 2916 | this.triggerChange(this.buildChangeDetails(old, this.data())); 2917 | } 2918 | } 2919 | } 2920 | }); 2921 | 2922 | $.fn.select2 = function () { 2923 | 2924 | var args = Array.prototype.slice.call(arguments, 0), 2925 | opts, 2926 | select2, 2927 | value, multiple, 2928 | allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "onSortStart", "onSortEnd", "enable", "readonly", "positionDropdown", "data"], 2929 | valueMethods = ["val", "opened", "isFocused", "container", "data"]; 2930 | 2931 | this.each(function () { 2932 | if (args.length === 0 || typeof(args[0]) === "object") { 2933 | opts = args.length === 0 ? {} : $.extend({}, args[0]); 2934 | opts.element = $(this); 2935 | 2936 | if (opts.element.get(0).tagName.toLowerCase() === "select") { 2937 | multiple = opts.element.prop("multiple"); 2938 | } else { 2939 | multiple = opts.multiple || false; 2940 | if ("tags" in opts) {opts.multiple = multiple = true;} 2941 | } 2942 | 2943 | select2 = multiple ? new MultiSelect2() : new SingleSelect2(); 2944 | select2.init(opts); 2945 | } else if (typeof(args[0]) === "string") { 2946 | 2947 | if (indexOf(args[0], allowedMethods) < 0) { 2948 | throw "Unknown method: " + args[0]; 2949 | } 2950 | 2951 | value = undefined; 2952 | select2 = $(this).data("select2"); 2953 | if (select2 === undefined) return; 2954 | if (args[0] === "container") { 2955 | value=select2.container; 2956 | } else { 2957 | value = select2[args[0]].apply(select2, args.slice(1)); 2958 | } 2959 | if (indexOf(args[0], valueMethods) >= 0) { 2960 | return false; 2961 | } 2962 | } else { 2963 | throw "Invalid arguments to select2 plugin: " + args; 2964 | } 2965 | }); 2966 | return (value === undefined) ? this : value; 2967 | }; 2968 | 2969 | // plugin defaults, accessible to users 2970 | $.fn.select2.defaults = { 2971 | width: "copy", 2972 | loadMorePadding: 0, 2973 | closeOnSelect: true, 2974 | openOnEnter: true, 2975 | containerCss: {}, 2976 | dropdownCss: {}, 2977 | containerCssClass: "", 2978 | dropdownCssClass: "", 2979 | formatResult: function(result, container, query, escapeMarkup) { 2980 | var markup=[]; 2981 | markMatch(result.text, query.term, markup, escapeMarkup); 2982 | return markup.join(""); 2983 | }, 2984 | formatSelection: function (data, container) { 2985 | return data ? data.text : undefined; 2986 | }, 2987 | sortResults: function (results, container, query) { 2988 | return results; 2989 | }, 2990 | formatResultCssClass: function(data) {return undefined;}, 2991 | formatNoMatches: function () { return "No matches found"; }, 2992 | formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " more character" + (n == 1? "" : "s"); }, 2993 | formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1? "" : "s"); }, 2994 | formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); }, 2995 | formatLoadMore: function (pageNumber) { return "Loading more results..."; }, 2996 | formatSearching: function () { return "Searching..."; }, 2997 | minimumResultsForSearch: 0, 2998 | minimumInputLength: 0, 2999 | maximumInputLength: null, 3000 | maximumSelectionSize: 0, 3001 | id: function (e) { return e.id; }, 3002 | matcher: function(term, text) { 3003 | return (''+text).toUpperCase().indexOf((''+term).toUpperCase()) >= 0; 3004 | }, 3005 | separator: ",", 3006 | tokenSeparators: [], 3007 | tokenizer: defaultTokenizer, 3008 | escapeMarkup: function (markup) { 3009 | var replace_map = { 3010 | '\\': '\', 3011 | '&': '&', 3012 | '<': '<', 3013 | '>': '>', 3014 | '"': '"', 3015 | "'": ''', 3016 | "/": '/' 3017 | }; 3018 | 3019 | return String(markup).replace(/[&<>"'\/\\]/g, function (match) { 3020 | return replace_map[match]; 3021 | }); 3022 | }, 3023 | blurOnChange: false, 3024 | selectOnBlur: false, 3025 | adaptContainerCssClass: function(c) { return c; }, 3026 | adaptDropdownCssClass: function(c) { return null; } 3027 | }; 3028 | 3029 | $.fn.select2.ajaxDefaults = { 3030 | transport: $.ajax, 3031 | params: { 3032 | type: "GET", 3033 | cache: false, 3034 | dataType: "json" 3035 | } 3036 | }; 3037 | 3038 | // exports 3039 | window.Select2 = { 3040 | query: { 3041 | ajax: ajax, 3042 | local: local, 3043 | tags: tags 3044 | }, util: { 3045 | debounce: debounce, 3046 | markMatch: markMatch 3047 | }, "class": { 3048 | "abstract": AbstractSelect2, 3049 | "single": SingleSelect2, 3050 | "multi": MultiSelect2 3051 | } 3052 | }; 3053 | 3054 | }(jQuery)); 3055 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | body { padding-top: 60px; } 15 | footer { margin-top: 40px; padding-top: 1em; border-top: 1px solid #aaa;} 16 | section { margin-top: 3em; border-top: 1px solid #eee; padding-top: 1em; } -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap_and_overrides.css.less: -------------------------------------------------------------------------------- 1 | @import "twitter/bootstrap/bootstrap"; 2 | @import "twitter/bootstrap/responsive"; 3 | 4 | // Set the correct sprite paths 5 | @iconSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings.png"); 6 | @iconWhiteSpritePath: asset-path("twitter/bootstrap/glyphicons-halflings-white.png"); 7 | 8 | // Set the Font Awesome (Font Awesome is default. You can disable by commenting below lines) 9 | // Note: If you use asset_path() here, your compiled bootstrap_and_overrides.css will not 10 | // have the proper paths. So for now we use the absolute path. 11 | @fontAwesomeEotPath: asset-path("fontawesome-webfont.eot?v=3.0.2"); 12 | @fontAwesomeEotPath_iefix: asset-path("fontawesome-webfont.eot?#iefix&v=3.0.2"); 13 | @fontAwesomeWoffPath: asset-path("fontawesome-webfont.woff?v=3.0.2"); 14 | @fontAwesomeTtfPath: asset-path("fontawesome-webfont.ttf?v=3.0.2"); 15 | 16 | // Font Awesome 17 | @import "fontawesome"; 18 | 19 | // Glyphicons 20 | //@import "twitter/bootstrap/sprites.less"; 21 | 22 | // Your custom LESS stylesheets goes here 23 | // 24 | // Since bootstrap was imported above you have access to its mixins which 25 | // you may use and inherit here 26 | // 27 | // If you'd like to override bootstrap's own variables, you can do so here as well 28 | // See http://twitter.github.com/bootstrap/customize.html#variables for their names and documentation 29 | // 30 | // Example: 31 | // @linkColor: #ff0000; 32 | -------------------------------------------------------------------------------- /app/assets/stylesheets/people.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the people controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 20px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/select2.css: -------------------------------------------------------------------------------- 1 | /* 2 | Version: 3.4.0 Timestamp: Tue May 14 08:27:33 PDT 2013 3 | */ 4 | .select2-container { 5 | margin: 0; 6 | position: relative; 7 | display: inline-block; 8 | /* inline-block for ie7 */ 9 | zoom: 1; 10 | *display: inline; 11 | vertical-align: middle; 12 | } 13 | 14 | .select2-container, 15 | .select2-drop, 16 | .select2-search, 17 | .select2-search input{ 18 | /* 19 | Force border-box so that % widths fit the parent 20 | container without overlap because of margin/padding. 21 | 22 | More Info : http://www.quirksmode.org/css/box.html 23 | */ 24 | -webkit-box-sizing: border-box; /* webkit */ 25 | -khtml-box-sizing: border-box; /* konqueror */ 26 | -moz-box-sizing: border-box; /* firefox */ 27 | -ms-box-sizing: border-box; /* ie */ 28 | box-sizing: border-box; /* css3 */ 29 | } 30 | 31 | .select2-container .select2-choice { 32 | display: block; 33 | height: 26px; 34 | padding: 0 0 0 8px; 35 | overflow: hidden; 36 | position: relative; 37 | 38 | border: 1px solid #aaa; 39 | white-space: nowrap; 40 | line-height: 26px; 41 | color: #444; 42 | text-decoration: none; 43 | 44 | -webkit-border-radius: 4px; 45 | -moz-border-radius: 4px; 46 | border-radius: 4px; 47 | 48 | -webkit-background-clip: padding-box; 49 | -moz-background-clip: padding; 50 | background-clip: padding-box; 51 | 52 | -webkit-touch-callout: none; 53 | -webkit-user-select: none; 54 | -khtml-user-select: none; 55 | -moz-user-select: none; 56 | -ms-user-select: none; 57 | user-select: none; 58 | 59 | background-color: #fff; 60 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); 61 | background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%); 62 | background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); 63 | background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%); 64 | background-image: -ms-linear-gradient(top, #ffffff 0%, #eeeeee 50%); 65 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0); 66 | background-image: linear-gradient(top, #ffffff 0%, #eeeeee 50%); 67 | } 68 | 69 | .select2-container.select2-drop-above .select2-choice { 70 | border-bottom-color: #aaa; 71 | 72 | -webkit-border-radius:0 0 4px 4px; 73 | -moz-border-radius:0 0 4px 4px; 74 | border-radius:0 0 4px 4px; 75 | 76 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white)); 77 | background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%); 78 | background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%); 79 | background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%); 80 | background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%); 81 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); 82 | background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%); 83 | } 84 | 85 | .select2-container.select2-allowclear .select2-choice span { 86 | margin-right: 42px; 87 | } 88 | 89 | .select2-container .select2-choice span { 90 | margin-right: 26px; 91 | display: block; 92 | overflow: hidden; 93 | 94 | white-space: nowrap; 95 | 96 | -ms-text-overflow: ellipsis; 97 | -o-text-overflow: ellipsis; 98 | text-overflow: ellipsis; 99 | } 100 | 101 | .select2-container .select2-choice abbr { 102 | display: none; 103 | width: 12px; 104 | height: 12px; 105 | position: absolute; 106 | right: 24px; 107 | top: 8px; 108 | 109 | font-size: 1px; 110 | text-decoration: none; 111 | 112 | border: 0; 113 | background: url('select2.png') right top no-repeat; 114 | cursor: pointer; 115 | outline: 0; 116 | } 117 | 118 | .select2-container.select2-allowclear .select2-choice abbr { 119 | display: inline-block; 120 | } 121 | 122 | .select2-container .select2-choice abbr:hover { 123 | background-position: right -11px; 124 | cursor: pointer; 125 | } 126 | 127 | .select2-drop-mask { 128 | position: absolute; 129 | left: 0; 130 | top: 0; 131 | z-index: 9998; 132 | } 133 | 134 | .select2-drop { 135 | width: 100%; 136 | margin-top:-1px; 137 | position: absolute; 138 | z-index: 9999; 139 | top: 100%; 140 | 141 | background: #fff; 142 | color: #000; 143 | border: 1px solid #aaa; 144 | border-top: 0; 145 | 146 | -webkit-border-radius: 0 0 4px 4px; 147 | -moz-border-radius: 0 0 4px 4px; 148 | border-radius: 0 0 4px 4px; 149 | 150 | -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 151 | -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 152 | box-shadow: 0 4px 5px rgba(0, 0, 0, .15); 153 | } 154 | 155 | .select2-drop-auto-width { 156 | border-top: 1px solid #aaa; 157 | width: auto; 158 | } 159 | 160 | .select2-drop-auto-width .select2-search { 161 | padding-top: 4px; 162 | } 163 | 164 | .select2-drop.select2-drop-above { 165 | margin-top: 1px; 166 | border-top: 1px solid #aaa; 167 | border-bottom: 0; 168 | 169 | -webkit-border-radius: 4px 4px 0 0; 170 | -moz-border-radius: 4px 4px 0 0; 171 | border-radius: 4px 4px 0 0; 172 | 173 | -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); 174 | -moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); 175 | box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); 176 | } 177 | 178 | .select2-container .select2-choice div { 179 | display: inline-block; 180 | width: 18px; 181 | height: 100%; 182 | position: absolute; 183 | right: 0; 184 | top: 0; 185 | 186 | border-left: 1px solid #aaa; 187 | -webkit-border-radius: 0 4px 4px 0; 188 | -moz-border-radius: 0 4px 4px 0; 189 | border-radius: 0 4px 4px 0; 190 | 191 | -webkit-background-clip: padding-box; 192 | -moz-background-clip: padding; 193 | background-clip: padding-box; 194 | 195 | background: #ccc; 196 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); 197 | background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); 198 | background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); 199 | background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); 200 | background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%); 201 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0); 202 | background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%); 203 | } 204 | 205 | .select2-container .select2-choice div b { 206 | display: block; 207 | width: 100%; 208 | height: 100%; 209 | background: url('select2.png') no-repeat 0 1px; 210 | } 211 | 212 | .select2-search { 213 | display: inline-block; 214 | width: 100%; 215 | min-height: 26px; 216 | margin: 0; 217 | padding-left: 4px; 218 | padding-right: 4px; 219 | 220 | position: relative; 221 | z-index: 10000; 222 | 223 | white-space: nowrap; 224 | } 225 | 226 | .select2-search input { 227 | width: 100%; 228 | height: auto !important; 229 | min-height: 26px; 230 | padding: 4px 20px 4px 5px; 231 | margin: 0; 232 | 233 | outline: 0; 234 | font-family: sans-serif; 235 | font-size: 1em; 236 | 237 | border: 1px solid #aaa; 238 | -webkit-border-radius: 0; 239 | -moz-border-radius: 0; 240 | border-radius: 0; 241 | 242 | -webkit-box-shadow: none; 243 | -moz-box-shadow: none; 244 | box-shadow: none; 245 | 246 | background: #fff url('select2.png') no-repeat 100% -22px; 247 | background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); 248 | background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); 249 | background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); 250 | background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); 251 | background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); 252 | background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%); 253 | } 254 | 255 | .select2-drop.select2-drop-above .select2-search input { 256 | margin-top: 4px; 257 | } 258 | 259 | .select2-search input.select2-active { 260 | background: #fff url('select2-spinner.gif') no-repeat 100%; 261 | background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); 262 | background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%); 263 | background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); 264 | background: url('select2-spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); 265 | background: url('select2-spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%); 266 | background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%); 267 | } 268 | 269 | .select2-container-active .select2-choice, 270 | .select2-container-active .select2-choices { 271 | border: 1px solid #5897fb; 272 | outline: none; 273 | 274 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); 275 | -moz-box-shadow: 0 0 5px rgba(0,0,0,.3); 276 | box-shadow: 0 0 5px rgba(0,0,0,.3); 277 | } 278 | 279 | .select2-dropdown-open .select2-choice { 280 | border-bottom-color: transparent; 281 | -webkit-box-shadow: 0 1px 0 #fff inset; 282 | -moz-box-shadow: 0 1px 0 #fff inset; 283 | box-shadow: 0 1px 0 #fff inset; 284 | 285 | -webkit-border-bottom-left-radius: 0; 286 | -moz-border-radius-bottomleft: 0; 287 | border-bottom-left-radius: 0; 288 | 289 | -webkit-border-bottom-right-radius: 0; 290 | -moz-border-radius-bottomright: 0; 291 | border-bottom-right-radius: 0; 292 | 293 | background-color: #eee; 294 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); 295 | background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%); 296 | background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); 297 | background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); 298 | background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%); 299 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); 300 | background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%); 301 | } 302 | 303 | .select2-dropdown-open.select2-drop-above .select2-choice, 304 | .select2-dropdown-open.select2-drop-above .select2-choices { 305 | border: 1px solid #5897fb; 306 | border-top-color: transparent; 307 | 308 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(0.5, #eeeeee)); 309 | background-image: -webkit-linear-gradient(center top, white 0%, #eeeeee 50%); 310 | background-image: -moz-linear-gradient(center top, white 0%, #eeeeee 50%); 311 | background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%); 312 | background-image: -ms-linear-gradient(bottom, #ffffff 0%,#eeeeee 50%); 313 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 ); 314 | background-image: linear-gradient(bottom, #ffffff 0%,#eeeeee 50%); 315 | } 316 | 317 | .select2-dropdown-open .select2-choice div { 318 | background: transparent; 319 | border-left: none; 320 | filter: none; 321 | } 322 | .select2-dropdown-open .select2-choice div b { 323 | background-position: -18px 1px; 324 | } 325 | 326 | /* results */ 327 | .select2-results { 328 | max-height: 200px; 329 | padding: 0 0 0 4px; 330 | margin: 4px 4px 4px 0; 331 | position: relative; 332 | overflow-x: hidden; 333 | overflow-y: auto; 334 | -webkit-tap-highlight-color: rgba(0,0,0,0); 335 | } 336 | 337 | .select2-results ul.select2-result-sub { 338 | margin: 0; 339 | padding-left: 0; 340 | } 341 | 342 | .select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px } 343 | .select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px } 344 | .select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px } 345 | .select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px } 346 | .select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px } 347 | .select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px } 348 | .select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px } 349 | 350 | .select2-results li { 351 | list-style: none; 352 | display: list-item; 353 | background-image: none; 354 | } 355 | 356 | .select2-results li.select2-result-with-children > .select2-result-label { 357 | font-weight: bold; 358 | } 359 | 360 | .select2-results .select2-result-label { 361 | padding: 3px 7px 4px; 362 | margin: 0; 363 | cursor: pointer; 364 | 365 | min-height: 1em; 366 | 367 | -webkit-touch-callout: none; 368 | -webkit-user-select: none; 369 | -khtml-user-select: none; 370 | -moz-user-select: none; 371 | -ms-user-select: none; 372 | user-select: none; 373 | } 374 | 375 | .select2-results .select2-highlighted { 376 | background: #3875d7; 377 | color: #fff; 378 | } 379 | 380 | .select2-results li em { 381 | background: #feffde; 382 | font-style: normal; 383 | } 384 | 385 | .select2-results .select2-highlighted em { 386 | background: transparent; 387 | } 388 | 389 | .select2-results .select2-highlighted ul { 390 | background: white; 391 | color: #000; 392 | } 393 | 394 | 395 | .select2-results .select2-no-results, 396 | .select2-results .select2-searching, 397 | .select2-results .select2-selection-limit { 398 | background: #f4f4f4; 399 | display: list-item; 400 | } 401 | 402 | /* 403 | disabled look for disabled choices in the results dropdown 404 | */ 405 | .select2-results .select2-disabled.select2-highlighted { 406 | color: #666; 407 | background: #f4f4f4; 408 | display: list-item; 409 | cursor: default; 410 | } 411 | .select2-results .select2-disabled { 412 | background: #f4f4f4; 413 | display: list-item; 414 | cursor: default; 415 | } 416 | 417 | .select2-results .select2-selected { 418 | display: none; 419 | } 420 | 421 | .select2-more-results.select2-active { 422 | background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%; 423 | } 424 | 425 | .select2-more-results { 426 | background: #f4f4f4; 427 | display: list-item; 428 | } 429 | 430 | /* disabled styles */ 431 | 432 | .select2-container.select2-container-disabled .select2-choice { 433 | background-color: #f4f4f4; 434 | background-image: none; 435 | border: 1px solid #ddd; 436 | cursor: default; 437 | } 438 | 439 | .select2-container.select2-container-disabled .select2-choice div { 440 | background-color: #f4f4f4; 441 | background-image: none; 442 | border-left: 0; 443 | } 444 | 445 | .select2-container.select2-container-disabled .select2-choice abbr { 446 | display: none; 447 | } 448 | 449 | 450 | /* multiselect */ 451 | 452 | .select2-container-multi .select2-choices { 453 | height: auto !important; 454 | height: 1%; 455 | margin: 0; 456 | padding: 0; 457 | position: relative; 458 | 459 | border: 1px solid #aaa; 460 | cursor: text; 461 | overflow: hidden; 462 | 463 | background-color: #fff; 464 | background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); 465 | background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 466 | background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 467 | background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 468 | background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%); 469 | background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%); 470 | } 471 | 472 | .select2-locked { 473 | padding: 3px 5px 3px 5px !important; 474 | } 475 | 476 | .select2-container-multi .select2-choices { 477 | min-height: 26px; 478 | } 479 | 480 | .select2-container-multi.select2-container-active .select2-choices { 481 | border: 1px solid #5897fb; 482 | outline: none; 483 | 484 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3); 485 | -moz-box-shadow: 0 0 5px rgba(0,0,0,.3); 486 | box-shadow: 0 0 5px rgba(0,0,0,.3); 487 | } 488 | .select2-container-multi .select2-choices li { 489 | float: left; 490 | list-style: none; 491 | } 492 | .select2-container-multi .select2-choices .select2-search-field { 493 | margin: 0; 494 | padding: 0; 495 | white-space: nowrap; 496 | } 497 | 498 | .select2-container-multi .select2-choices .select2-search-field input { 499 | padding: 5px; 500 | margin: 1px 0; 501 | 502 | font-family: sans-serif; 503 | font-size: 100%; 504 | color: #666; 505 | outline: 0; 506 | border: 0; 507 | -webkit-box-shadow: none; 508 | -moz-box-shadow: none; 509 | box-shadow: none; 510 | background: transparent !important; 511 | } 512 | 513 | .select2-container-multi .select2-choices .select2-search-field input.select2-active { 514 | background: #fff url('select2-spinner.gif') no-repeat 100% !important; 515 | } 516 | 517 | .select2-default { 518 | color: #999 !important; 519 | } 520 | 521 | .select2-container-multi .select2-choices .select2-search-choice { 522 | padding: 3px 5px 3px 18px; 523 | margin: 3px 0 3px 5px; 524 | position: relative; 525 | 526 | line-height: 13px; 527 | color: #333; 528 | cursor: default; 529 | border: 1px solid #aaaaaa; 530 | 531 | -webkit-border-radius: 3px; 532 | -moz-border-radius: 3px; 533 | border-radius: 3px; 534 | 535 | -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); 536 | -moz-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); 537 | box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05); 538 | 539 | -webkit-background-clip: padding-box; 540 | -moz-background-clip: padding; 541 | background-clip: padding-box; 542 | 543 | -webkit-touch-callout: none; 544 | -webkit-user-select: none; 545 | -khtml-user-select: none; 546 | -moz-user-select: none; 547 | -ms-user-select: none; 548 | user-select: none; 549 | 550 | background-color: #e4e4e4; 551 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0 ); 552 | background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); 553 | background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 554 | background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 555 | background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 556 | background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 557 | background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); 558 | } 559 | .select2-container-multi .select2-choices .select2-search-choice span { 560 | cursor: default; 561 | } 562 | .select2-container-multi .select2-choices .select2-search-choice-focus { 563 | background: #d4d4d4; 564 | } 565 | 566 | .select2-search-choice-close { 567 | display: block; 568 | width: 12px; 569 | height: 13px; 570 | position: absolute; 571 | right: 3px; 572 | top: 4px; 573 | 574 | font-size: 1px; 575 | outline: none; 576 | background: url('select2.png') right top no-repeat; 577 | } 578 | 579 | .select2-container-multi .select2-search-choice-close { 580 | left: 3px; 581 | } 582 | 583 | .select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { 584 | background-position: right -11px; 585 | } 586 | .select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { 587 | background-position: right -11px; 588 | } 589 | 590 | /* disabled styles */ 591 | .select2-container-multi.select2-container-disabled .select2-choices{ 592 | background-color: #f4f4f4; 593 | background-image: none; 594 | border: 1px solid #ddd; 595 | cursor: default; 596 | } 597 | 598 | .select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { 599 | padding: 3px 5px 3px 5px; 600 | border: 1px solid #ddd; 601 | background-image: none; 602 | background-color: #f4f4f4; 603 | } 604 | 605 | .select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none; 606 | background:none; 607 | } 608 | /* end multiselect */ 609 | 610 | 611 | .select2-result-selectable .select2-match, 612 | .select2-result-unselectable .select2-match { 613 | text-decoration: underline; 614 | } 615 | 616 | .select2-offscreen, .select2-offscreen:focus { 617 | clip: rect(0 0 0 0); 618 | width: 1px; 619 | height: 1px; 620 | border: 0; 621 | margin: 0; 622 | padding: 0; 623 | overflow: hidden; 624 | position: absolute; 625 | outline: 0; 626 | left: 0px; 627 | } 628 | 629 | .select2-display-none { 630 | display: none; 631 | } 632 | 633 | .select2-measure-scrollbar { 634 | position: absolute; 635 | top: -10000px; 636 | left: -10000px; 637 | width: 100px; 638 | height: 100px; 639 | overflow: scroll; 640 | } 641 | /* Retina-ize icons */ 642 | 643 | @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) { 644 | .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice div b { 645 | background-image: url('select2x2.png') !important; 646 | background-repeat: no-repeat !important; 647 | background-size: 60px 40px !important; 648 | } 649 | .select2-search input { 650 | background-position: 100% -21px !important; 651 | } 652 | } 653 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/people_controller.rb: -------------------------------------------------------------------------------- 1 | class PeopleController < ApplicationController 2 | respond_to :html, :json 3 | 4 | def index 5 | @people = Person.order('name asc').all 6 | respond_with @people 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/people_helper.rb: -------------------------------------------------------------------------------- 1 | module PeopleHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/person.rb: -------------------------------------------------------------------------------- 1 | class Person < ActiveRecord::Base 2 | attr_accessible :name, :favorite_color 3 | end 4 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= content_for?(:title) ? yield(:title) : "Select2 Rails Example" %> 8 | <%= csrf_meta_tags %> 9 | 10 | 13 | <%= stylesheet_link_tag "application", :media => "all" %> 14 | 15 | 16 | 17 | 31 | 32 |
    33 |
    34 |
    35 | <%= bootstrap_flash %> 36 | <%= yield %> 37 |
    38 |
    39 | 40 | 46 | 47 |
    48 | 49 | 51 | 52 | <%= javascript_include_tag "application" %> 53 | <%= yield :js %> 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/views/people/index.html.erb: -------------------------------------------------------------------------------- 1 | 4 | 5 |

    Sample Data

    6 |

    This can be found in the create_people migration.

    7 | 8 | 9 | 10 | 11 | 12 | <% @people.each do |person| %> 13 | 14 | 15 | 16 | 17 | <% end %> 18 |
    NameFavorite Color
    <%= person.name %><%= person.favorite_color %>
    19 | 20 |
    21 | 22 |
    23 |

    Simple Example

    24 |
    25 |
    26 |

    Without Select2

    27 |
    28 | <%= select_tag "people", options_from_collection_for_select(@people, "id", "name") %> 29 |
    30 | 31 |
    32 |
    33 |

    With Select2

    34 |
    35 | <%= select_tag "people", options_from_collection_for_select(@people, "id", "name"), id: "simple-example" %> 36 |
    37 |
    38 |
    39 | 40 |

    This is a vanilla select_tag using a rails generator. The code to initialize this is:

    41 |
     42 | $(document).ready(function() {
     43 |   $('select#simple-example').select2();
     44 | });
     45 | 
    46 |
    47 | 48 | 49 |
    50 |

    Placeholder Example

    51 |
    52 | <%= select_tag "people-placeholder", options_from_collection_for_select(@people, "id", "name"), include_blank: true, id: "placeholder-example", data: { placeholder: "Choose a person" } %> 53 |
    54 | 55 |
     56 | $(document).ready(function() {
     57 |   $('select#placeholder-example').select2({
     58 |     placeholder: "Choose a person",
     59 |     allowClear: true
     60 |   });
     61 | });
     62 | 
    63 | 64 |

    When using the placeholder option with Rails, it is necessary to use the include_blank: true option to your select_tag options. The allowClear option will place a small X to the right which allows the selection to be cleared.

    65 |
    66 | 67 |
    68 |

    Template Example

    69 |
    70 | 75 |
    76 | 77 |

    Ruby/HTML

    78 |
     79 | <select id="template-example">
     80 |   <% @people.each do |person| %>
     81 |     <option value="<%= person.id %>" data-favorite-color="<%= person.favorite_color %>"><%= person.name %></option>
     82 |   <% end %>
     83 | </select>
     84 | 
    85 | 86 |

    JavaScript

    87 |
     88 | $(document).ready(function() {
     89 |   function formatExample(person) {
     90 |     var originalOption = $(person.element);
     91 |     return "   " + person.text;
     92 |   }
     93 |   $('select#template-example').select2({
     94 |     formatResult: formatExample,
     95 |     formatSelection: formatExample,
     96 |     escapeMarkup: function(m) { return m; }
     97 |   });
     98 | });
     99 | 
    100 | 101 |

    102 | In this example, we're not using any Rails helpers to create a select tag. We 103 | manually iterate through the @people collection and write out our 104 | specific option tags. We do this so that we can pass the data-favorite-color attribute in to the markup. 105 |

    106 | 107 |

    In our JavaScript for this example, we use formatExample function to add a small span to our list containing the person's favorite color. The span uses a background color and space characters to display. We use a cringe-worthy inline style to make it look good (hey, at least it works!). Finally, we call the select2 function and pass it the formatExample function to format our data. Select2 takes care of the rest.

    108 |
    109 | 110 | 111 | 112 |
    113 |

    Ajax Example

    114 |
    115 | 116 |
    117 | 118 |

    Rails

    119 |
    120 | class PeopleController < ApplicationController
    121 |   respond_to :html, :json
    122 | 
    123 |   def index
    124 |     @people = Person.order('name asc').all
    125 |     respond_with @people
    126 |   end
    127 | end
    128 | 
    129 | 130 |

    HTML

    131 |
    132 | <input type="hidden" id="ajax-example" />
    133 | 
    134 | 135 |

    JavaScript

    136 |
    137 | $(document).ready(function() {
    138 |   $('#ajax-example').select2({
    139 |     ajax: {
    140 |       url: "<%= people_path(format: 'json') %>",
    141 |       dataType: "json",
    142 |       results: function(data, page) {
    143 |         return { 
    144 |           results: $.map( data, function(person, i) { 
    145 |             return { id: person.id, text: person.name } 
    146 |           } )
    147 |         }
    148 |       }
    149 |     }
    150 |   });
    151 | });
    152 | 
    153 | 154 |

    In this example, we are using Rails as the backend to return json to the select2 call. In our HTML, we are creating a hidden input with the id of ajax-example. We then call the select2 function on that element.

    155 | 156 |

    The biggest difference between this example and the others is that we are passing in the ajax option to select2 this time. We tell select2 the url we want to fetch with the url key. We just use a Rails helper to call the people path and pass in the json format. We then use this in the dataType key.

    157 | 158 |

    The next part, on line 8, is the results key. This is where we perform a little bit of manipulation on the data. Select2 expects that the results object will have an id and text key for each result. We go through our data response, which we get from our Rails controller, and create this object for select2 using the jQuery map function.

    159 | 160 |

    Our Rails controller responds with json of the people collection. We could use any Rails controller here as long as it returns json.

    161 |
    162 | 163 | 164 | 165 | 166 | <% content_for :js do %> 167 | 201 | <% end %> -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Select2::Application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | if defined?(Bundler) 6 | # If you precompile assets before deploying to production, use this line 7 | Bundler.require(*Rails.groups(:assets => %w(development test))) 8 | # If you want your assets lazily compiled in production, use this line 9 | # Bundler.require(:default, :assets, Rails.env) 10 | end 11 | 12 | module Select2 13 | class Application < Rails::Application 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | # Custom directories with classes and modules you want to be autoloadable. 19 | # config.autoload_paths += %W(#{config.root}/extras) 20 | 21 | # Only load the plugins named here, in the order given (default is alphabetical). 22 | # :all can be used as a placeholder for all plugins not explicitly named. 23 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 24 | 25 | # Activate observers that should always be running. 26 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 27 | 28 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 29 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 30 | # config.time_zone = 'Central Time (US & Canada)' 31 | 32 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 33 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 34 | # config.i18n.default_locale = :de 35 | 36 | # Configure the default encoding used in templates for Ruby 1.9. 37 | config.encoding = "utf-8" 38 | 39 | # Configure sensitive parameters which will be filtered from the log file. 40 | config.filter_parameters += [:password] 41 | 42 | # Enable escaping HTML in JSON. 43 | config.active_support.escape_html_entities_in_json = true 44 | 45 | # Use SQL instead of Active Record's schema dumper when creating the database. 46 | # This is necessary if your schema can't be completely dumped by the schema dumper, 47 | # like if you have constraints or database-specific column types 48 | # config.active_record.schema_format = :sql 49 | 50 | # Enforce whitelist mode for mass assignment. 51 | # This will create an empty whitelist of attributes available for mass-assignment for all models 52 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible 53 | # parameters by using an attr_accessible or attr_protected declaration. 54 | config.active_record.whitelist_attributes = true 55 | 56 | # Enable the asset pipeline 57 | config.assets.enabled = true 58 | 59 | # Version of your assets, change this if you want to expire all your assets 60 | config.assets.version = '1.0' 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | Select2::Application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Select2::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Raise exception on mass assignment protection for Active Record models 26 | config.active_record.mass_assignment_sanitizer = :strict 27 | 28 | # Log the query plan for queries taking more than this (works 29 | # with SQLite, MySQL, and PostgreSQL) 30 | config.active_record.auto_explain_threshold_in_seconds = 0.5 31 | 32 | # Do not compress assets 33 | config.assets.compress = false 34 | 35 | # Expands the lines which load the assets 36 | config.assets.debug = true 37 | end 38 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Select2::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # Code is not reloaded between requests 5 | config.cache_classes = true 6 | 7 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to nil and saved in location specified by config.assets.prefix 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | # config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Prepend all log lines with the following tags 37 | # config.log_tags = [ :subdomain, :uuid ] 38 | 39 | # Use a different logger for distributed setups 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 41 | 42 | # Use a different cache store in production 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 46 | # config.action_controller.asset_host = "http://assets.example.com" 47 | 48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 49 | # config.assets.precompile += %w( search.js ) 50 | 51 | # Disable delivery errors, bad email addresses will be ignored 52 | # config.action_mailer.raise_delivery_errors = false 53 | 54 | # Enable threaded mode 55 | # config.threadsafe! 56 | 57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 58 | # the I18n.default_locale when a translation can not be found) 59 | config.i18n.fallbacks = true 60 | 61 | # Send deprecation notices to registered listeners 62 | config.active_support.deprecation = :notify 63 | 64 | # Log the query plan for queries taking more than this (works 65 | # with SQLite, MySQL, and PostgreSQL) 66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 67 | end 68 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Select2::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Log error messages when you accidentally call methods on nil 15 | config.whiny_nils = true 16 | 17 | # Show full error reports and disable caching 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Raise exceptions instead of rendering exception templates 22 | config.action_dispatch.show_exceptions = false 23 | 24 | # Disable request forgery protection in test environment 25 | config.action_controller.allow_forgery_protection = false 26 | 27 | # Tell Action Mailer not to deliver emails to the real world. 28 | # The :test delivery method accumulates sent emails in the 29 | # ActionMailer::Base.deliveries array. 30 | config.action_mailer.delivery_method = :test 31 | 32 | # Raise exception on mass assignment protection for Active Record models 33 | config.active_record.mass_assignment_sanitizer = :strict 34 | 35 | # Print deprecation notices to the stderr 36 | config.active_support.deprecation = :stderr 37 | end 38 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Select2::Application.config.secret_token = '8cf452db27276d4f3e56fd0a7955d181f121701f3620273767629bd441302d1c0be97c2a7ae909d41500803f9ba0e6f686b8610edd53fcefb8f3af89234fbab3' 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Select2::Application.config.session_store :cookie_store, key: '_select2_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Select2::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /config/locales/en.bootstrap.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | helpers: 6 | actions: "Actions" 7 | links: 8 | back: "Back" 9 | cancel: "Cancel" 10 | confirm: "Are you sure?" 11 | destroy: "Delete" 12 | new: "New" 13 | titles: 14 | edit: "Edit" 15 | save: "Save" 16 | new: "New" 17 | delete: "Delete" 18 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | hello: "Hello world" 6 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Select2::Application.routes.draw do 2 | resources :people, only: [:index] 3 | root to: 'people#index' 4 | 5 | # The priority is based upon order of creation: 6 | # first created -> highest priority. 7 | 8 | # Sample of regular route: 9 | # match 'products/:id' => 'catalog#view' 10 | # Keep in mind you can assign values other than :controller and :action 11 | 12 | # Sample of named route: 13 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 14 | # This route can be invoked with purchase_url(:id => product.id) 15 | 16 | # Sample resource route (maps HTTP verbs to controller actions automatically): 17 | # resources :products 18 | 19 | # Sample resource route with options: 20 | # resources :products do 21 | # member do 22 | # get 'short' 23 | # post 'toggle' 24 | # end 25 | # 26 | # collection do 27 | # get 'sold' 28 | # end 29 | # end 30 | 31 | # Sample resource route with sub-resources: 32 | # resources :products do 33 | # resources :comments, :sales 34 | # resource :seller 35 | # end 36 | 37 | # Sample resource route with more complex sub-resources 38 | # resources :products do 39 | # resources :comments 40 | # resources :sales do 41 | # get 'recent', :on => :collection 42 | # end 43 | # end 44 | 45 | # Sample resource route within a namespace: 46 | # namespace :admin do 47 | # # Directs /admin/products/* to Admin::ProductsController 48 | # # (app/controllers/admin/products_controller.rb) 49 | # resources :products 50 | # end 51 | 52 | # You can have the root of your site routed with "root" 53 | # just remember to delete public/index.html. 54 | # root :to => 'welcome#index' 55 | 56 | # See how all your routes lay out with "rake routes" 57 | 58 | # This is a legacy wild controller route that's not recommended for RESTful applications. 59 | # Note: This route will make all actions in every controller accessible via GET requests. 60 | # match ':controller(/:action(/:id))(.:format)' 61 | end 62 | -------------------------------------------------------------------------------- /db/migrate/20130603161159_create_people.rb: -------------------------------------------------------------------------------- 1 | class CreatePeople < ActiveRecord::Migration 2 | def change 3 | create_table :people do |t| 4 | t.string :name 5 | t.string :favorite_color 6 | t.timestamps 7 | end 8 | 9 | Person.create name: "Jason", favorite_color: "Blue" 10 | Person.create name: "Mike", favorite_color: "Red" 11 | Person.create name: "Paige", favorite_color: "Purple" 12 | Person.create name: "Eric", favorite_color: "Black" 13 | Person.create name: "Nick", favorite_color: "Orange" 14 | Person.create name: "Brittney", favorite_color: "Yellow" 15 | Person.create name: "Fred", favorite_color: "Tan" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended to check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(:version => 20130603161159) do 15 | 16 | create_table "people", :force => true do |t| 17 | t.string "name" 18 | t.string "favorite_color" 19 | t.datetime "created_at", :null => false 20 | t.datetime "updated_at", :null => false 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/log/.gitkeep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    The page you were looking for doesn't exist.

    23 |

    You may have mistyped the address or the page may have moved.

    24 |
    25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    The change you wanted was rejected.

    23 |

    Maybe you tried to change something you didn't have access to.

    24 |
    25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    We're sorry, but something went wrong.

    23 |
    24 | 25 | 26 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/test/fixtures/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/people.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /test/functional/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/test/functional/.gitkeep -------------------------------------------------------------------------------- /test/functional/people_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PeopleControllerTest < ActionController::TestCase 4 | test "should get index" do 5 | get :index 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/test/integration/.gitkeep -------------------------------------------------------------------------------- /test/performance/browsing_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'rails/performance_test_help' 3 | 4 | class BrowsingTest < ActionDispatch::PerformanceTest 5 | # Refer to the documentation for all available options 6 | # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory] 7 | # :output => 'tmp/performance', :formats => [:flat] } 8 | 9 | def test_homepage 10 | get '/' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] = "test" 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. 7 | # 8 | # Note: You'll currently still have to declare fixtures explicitly in integration tests 9 | # -- they do not yet inherit this setting 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | end 14 | -------------------------------------------------------------------------------- /test/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/test/unit/.gitkeep -------------------------------------------------------------------------------- /test/unit/helpers/people_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PeopleHelperTest < ActionView::TestCase 4 | end 5 | -------------------------------------------------------------------------------- /test/unit/person_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PersonTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jseifer/rails-select2-example/92d0302bd01e42cf638033c4ab9b01d75de80409/vendor/plugins/.gitkeep --------------------------------------------------------------------------------