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