Line 0
Link Here
|
0 |
- |
1 |
/* |
|
|
2 |
* jQuery autofill plugin |
3 |
* |
4 |
* Automaticly fills form inputs with relevant data based on search result |
5 |
*/ |
6 |
|
7 |
(function ($) { |
8 |
function typeString(o) { |
9 |
if (typeof o != 'object') |
10 |
return typeof o; |
11 |
|
12 |
if (o === null) |
13 |
return "null"; |
14 |
|
15 |
//object, array, function, date, regexp, string, number, boolean, error |
16 |
var internalClass = Object.prototype.toString.call(o) |
17 |
.match(/\[object\s(\w+)\]/)[1]; |
18 |
|
19 |
return internalClass.toLowerCase(); |
20 |
} |
21 |
|
22 |
var AutoFiller = function (elm, fields, type) { |
23 |
var self = this; |
24 |
self.type = type; |
25 |
self.$elm = $(elm); |
26 |
self.fields = fields; |
27 |
|
28 |
/* decorate element as autofiller */ |
29 |
var $inputgroup = $('<div class="input-prepend">'); |
30 |
self.$undo = $('<span class="add-on" style="display:none;cursor:pointer;">undo</span>'); |
31 |
self.$fillbtn = $('<span class="add-on"><i class="icon-edit" style="cursor:pointer;"></i></span>'); |
32 |
self.$error = $('<span class="add-on" style="display:none;"></span>'); |
33 |
self.$elm.wrap($inputgroup); |
34 |
self.$elm.after(self.$error); |
35 |
self.$elm.after(self.$undo); |
36 |
self.$elm.after(self.$fillbtn); |
37 |
|
38 |
for(var key in fields) { |
39 |
if( fields.hasOwnProperty(key) && typeof fields[key] === 'object' ) { |
40 |
var $target = $('#' + self.fields[key].target); |
41 |
self.fields[key].$target = $target; |
42 |
} |
43 |
} |
44 |
|
45 |
self.$fillbtn.click(function(){ |
46 |
/* only allow forced update once every second */ |
47 |
if(Date.now() - self.lastupdate > 1000) { |
48 |
self.$elm.trigger('change'); |
49 |
} |
50 |
}); |
51 |
|
52 |
self.$undo.click(function(){ |
53 |
for(var key in self.fields) { |
54 |
var field = self.fields[key]; |
55 |
field.$target.trigger('autofill-undo'); |
56 |
} |
57 |
self.$undo.hide(); |
58 |
}); |
59 |
|
60 |
self.$elm.change(function() { |
61 |
self.lastupdate = Date.now(); |
62 |
self.$error.html(''); |
63 |
self.$error.hide(); |
64 |
/* give user some feedback that the request is in progress */ |
65 |
self.$fillbtn.fadeOut(1000).fadeIn(1000); |
66 |
|
67 |
var gAPI = 'https://www.googleapis.com/books/v1/volumes?q='; |
68 |
if(self.type) |
69 |
gAPI += self.type + ':'; |
70 |
gAPI += self.$elm.val(); |
71 |
gAPI += '&maxResults=1'; |
72 |
|
73 |
$.getJSON(gAPI, function (response) { |
74 |
if(response.totalItems == 0) { |
75 |
self.$error.html('Sorry, nothing found.'); |
76 |
self.$error.show(); |
77 |
return; |
78 |
} |
79 |
|
80 |
var undos = 0; |
81 |
var item = response.items[0]; |
82 |
for(var key in self.fields) { |
83 |
var filled = false; |
84 |
var value = eval('item.'+key); |
85 |
var field = self.fields[key]; |
86 |
|
87 |
/* field handled by caller */ |
88 |
if('handle' in field) { |
89 |
if(typeof field.handle === 'function') |
90 |
field.handle(field.$target, value); |
91 |
|
92 |
continue; /* next please */ |
93 |
} |
94 |
|
95 |
/* wouldn't know what to do with result unless we have a |
96 |
* target */ |
97 |
if( ! field.$target ) |
98 |
continue; |
99 |
|
100 |
/* handle differently depending on datatype */ |
101 |
switch(typeString(value)) { |
102 |
case 'array': |
103 |
switch(field.$target.prop('nodeName').toUpperCase()) { |
104 |
case 'TEXTAREA': |
105 |
undos++; |
106 |
field.$target.bind('autofill-undo', field.$target.text(), function(e){$(this).text(e.data);}); |
107 |
field.$target.text(value.join(', ')); |
108 |
break; |
109 |
case 'INPUT': |
110 |
default: |
111 |
undos++; |
112 |
field.$target.bind('autofill-undo', field.$target.val(), function(e){$(this).val(e.data);}); |
113 |
field.$target.val(value.join(', ')); |
114 |
break; |
115 |
} |
116 |
break; |
117 |
default: |
118 |
switch(field.$target.prop('nodeName').toUpperCase()) { |
119 |
case 'TEXTAREA': |
120 |
undos++; |
121 |
field.$target.bind('autofill-undo', field.$target.text(), function(e){$(this).text(e.data);}); |
122 |
field.$target.text(value); |
123 |
break; |
124 |
case 'SELECT': |
125 |
case 'INPUT': |
126 |
default: |
127 |
undos++; |
128 |
field.$target.bind('autofill-undo', field.$target.val(), function(e){$(this).val(e.data);}); |
129 |
field.$target.val(value); |
130 |
break; |
131 |
} |
132 |
} |
133 |
|
134 |
switch(field.effect) { |
135 |
case 'flash': |
136 |
field.$target.fadeOut(500).fadeIn(500); |
137 |
break; |
138 |
} |
139 |
} |
140 |
|
141 |
if(undos > 0) |
142 |
self.$undo.show(); |
143 |
|
144 |
}); |
145 |
}); |
146 |
}; |
147 |
|
148 |
/* |
149 |
* @fields object: Google Books API item propreties map for |
150 |
* mapping against a target element. Expected |
151 |
* type: |
152 |
* { |
153 |
* GoogleBooksItem.property: {target: ELEM, |
154 |
* handle: function(target, value), |
155 |
* effect: jQuery effects, |
156 |
* } |
157 |
* } |
158 |
* |
159 |
* "target" is optional and if specified alone (i.e no |
160 |
* handle proprety) autofill will automaticly fill this |
161 |
* target element with returned data. |
162 |
* |
163 |
* "handle" is optional and will be called when ajax request |
164 |
* has finished and target is matched. Function specifies |
165 |
* two arguments: target and value. Target is the target |
166 |
* element specified by "target" and value is the value |
167 |
* returned by Google Books API for the matched property. |
168 |
* |
169 |
* If a handle function is given, full control of result data |
170 |
* is given to the handle function. |
171 |
* |
172 |
* "effect" is optional and specifies effect name of effect |
173 |
* to use for the target once value has been set. Can be one of: |
174 |
* |
175 |
* - 'flash' |
176 |
* |
177 |
* @type string: defines the query type, default to input name |
178 |
* For example <input type="text" name="isbn"></input> |
179 |
* will search for isbn by default |
180 |
* |
181 |
* @EXAMPLE |
182 |
* |
183 |
* $('#isbn').autofill({ |
184 |
* 'volumeInfo.title': {target: 'title', effect: 'flash'}, |
185 |
* 'volumeInfo.authors': {target: 'author'}, |
186 |
* 'volumeInfo.publisher': {target: 'publishercode'}, |
187 |
* 'selfLink': {handle: function(t,v){window.location=v;}} |
188 |
* }); |
189 |
* */ |
190 |
$.fn.autofill = function(fields, type) { |
191 |
if(type === undefined) // default to input name |
192 |
type = this.attr('name'); |
193 |
|
194 |
return this.each(function(i){ |
195 |
var plugin = $.data(this, "plugin_autofill"); |
196 |
if (plugin) |
197 |
plugin.destroy(); |
198 |
|
199 |
$.data(this, "plugin_autofill", new AutoFiller(this, fields, type)); |
200 |
}); |
201 |
}; |
202 |
}(jQuery)); |