1/*global checkSaveStatuses, console, deparam, path, Recaptcha, vufindString */ 2 3var Lightbox = { 4 /** 5 * We save the URL and POST data every time we call getByUrl. 6 * If we don't have a target a form submission, we use these variables 7 * to replicate empty target behaviour by submitting to the current "page". 8 */ 9 lastURL: false, 10 lastPOST: false, 11 openingURL: false, 12 shown: false, // Is the lightbox deployed? 13 XHR: false, // Used for current in-progress XHR lightbox request 14 openStack: [], // Array of functions to be called after changeContent or the lightbox event 'shown' 15 closeStack: [], // Array of functions to be called and cleared after the lightbox event 'hidden' 16 formHandlers: [], // Full custom handlers for forms; by name 17 formCallbacks: [], // Custom functions for forms, called after .submit(); by name 18 19 /**********************************/ 20 /* ====== INTERFACE ====== */ 21 /**********************************/ 22 /** 23 * Register custom open event handlers 24 * 25 * Think of this as the $(document).ready() of the Lightbox 26 * There's actually an alias right below it. 27 * 28 * If your template has inline JS, $(document).ready() will not fire in the lightbox 29 * because it already fired before you opened the lightbox and won't fire again. 30 * 31 * You can use $.isReady to determine if ready() has already been called 32 * so you can trigger your function immediately in the inline JS. 33 */ 34 addOpenAction: function(func) { 35 this.openStack.push(func); 36 }, 37 // Alias for addOpenAction 38 ready: function(func) { 39 this.openStack.push(func); 40 }, 41 /** 42 * Register custom close event handlers 43 */ 44 addCloseAction: function(func) { 45 this.closeStack.push(func); 46 }, 47 /** 48 * For when you want to handle that form all by yourself 49 * 50 * We recommend using the getLightbox action in the AJAX Controller 51 * to ensure you get a lightbox formatted page. 52 * 53 * If your handler doesn't return false, the form will submit the normal way, 54 * with all normal behavior that goes with: redirection, etc. 55 */ 56 addFormHandler: function(formName, func) { 57 this.formHandlers[formName] = func; 58 }, 59 /** 60 * Register a function to be called when a form submission succeeds 61 * 62 * We add error checking by default, you never know when error blocks will strike. 63 * Passing false to expectsError turns this off. Errors are inserted above *current* content. 64 */ 65 addFormCallback: function(formName, func, expectsError) { 66 if(typeof expectsError === "undefined" || expectsError) { 67 this.formCallbacks[formName] = function(html) { 68 Lightbox.checkForError(html, func); 69 }; 70 } else { 71 this.formCallbacks[formName] = func; 72 } 73 }, 74 /** 75 * We store all the ajax calls in case we need to cancel. 76 * This function cancels the previous call and creates a new one. 77 */ 78 ajax: function(obj) { 79 if(this.XHR) { 80 this.XHR.abort(); 81 } 82 this.XHR = $.ajax(obj); 83 }, 84 /**********************************/ 85 /* ====== LIGHTBOX ACTIONS ====== */ 86 /**********************************/ 87 /** 88 * Change the content of the lightbox. 89 * 90 * Hide the header if it's empty to make more 91 * room for content and avoid double headers. 92 */ 93 titleSet: false, 94 changeContent: function(html) { 95 var header = $('#modal .modal-header'); 96 if(!Lightbox.titleSet) { 97 var h2 = html.match(/<h2>([^<]*)<\/h2>/); 98 if(h2) { 99 header.find('.modal-title').html(h2[1]); 100 } else { 101 var pLead = html.match(/<p class="lead[^>]*>([^<]*)<\/p>/); 102 if(pLead) { 103 header.find('.modal-title').html(pLead[1]); 104 } 105 } 106 Lightbox.titleSet = false; 107 } 108 if(header.find('.modal-title').html().length == 0) { 109 header.css('border-bottom-width', '0'); 110 } else { 111 header.css('border-bottom-width', '1px'); 112 } 113 $('#modal .modal-body').html(html).modal({'show':true,'backdrop':false}); 114 Lightbox.openActions(); 115 }, 116 117 /** 118 * This is the function you call to manually close the lightbox 119 */ 120 close: function(evt) { 121 $('#modal').modal('hide'); // This event calls closeActions 122 }, 123 /** 124 * This function is attached to the lightbox close event, 125 * so it always runs when the lightbox is closed. 126 */ 127 closeActions: function() { 128 Lightbox.shown = false; 129 Lightbox.openingURL = false; 130 // Clean out stack 131 while(Lightbox.closeStack.length > 0) { 132 var f = Lightbox.closeStack.pop(); 133 f(); 134 } 135 if(this.XHR) { this.XHR.abort(); } 136 // Reset content so we start fresh when we open a lightbox 137 $('#modal').removeData('modal');
138 $('#modal').find('.modal-title').html(' '); 139 $('#modal').find('.modal-body').html(vufindString.loading + "..."); 140 }, 141 /** 142 * Call all the functions we need for when the modal loads 143 * 144 * Called by the 'shown' event and at the end of changeContent 145 */ 146 openActions: function() { 147 for(var i=0;i<Lightbox.openStack.length;i++) { 148 Lightbox.openStack[i](); 149 } 150 }, 151 /** 152 * This function changes the content of the lightbox to a message with a close button 153 */ 154 confirm: function(message) { 155 this.changeContent('<div class="alert alert-info">'+message+'</div><button class="btn btn-default" onClick="Lightbox.close()">'+vufindString['close']+'</button>'); 156 }, 157 success: function(message) { 158 this.changeContent('<div class="alert alert-success">'+message+'</div><button class="btn btn-default" onClick="Lightbox.close()">'+vufindString['close']+'</button>'); 159 }, 160 /** 161 * Regexes a piece of html to find an error alert 162 * If one is found, display it 163 * 164 * If one is not found, return html to a success callback function 165 */ 166 checkForError: function(html, success, type) { 167 if(typeof type === "undefined") { 168 type = "danger"; 169 } 170 var divPattern = '<div class="alert alert-'+type+'">'; 171 var fi = html.indexOf(divPattern); 172 if(fi > -1) { 173 var li = html.indexOf('</div>', fi+divPattern.length); 174 Lightbox.displayError(html.substring(fi+divPattern.length, li).replace(/^[\s<>]+|[\s<>]+$/g, ''), type); 175 } else { 176 success(html); 177 } 178 }, 179 /** 180 * Insert an error alert element at the top of the lightbox 181 */ 182 displayError: function(message, type) { 183 if(typeof type === "undefined") { 184 type = "danger"; 185 } 186 $('#modal .modal-body .alert').remove(); 187 var html = $.parseHTML($('#modal .modal-body').html()); 188 // Empty or alert only, change to message with button 189 if($('#modal .modal-body').html() == vufindString.loading+"..." 190 || (html.length == 1 && $(html).hasClass('alert-'+type))) { 191 Lightbox.changeContent('<div class="alert alert-'+type+'" role="alert">'+message+'</div><button class="btn btn-default" onClick="Lightbox.close()">'+vufindString['close']+'</button>'); 192 // Page without alert 193 } else { 194 $('#modal .modal-body').prepend('<div class="alert alert-'+type+' alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button><p class="message">'+message+'</p></div>'); 195 } 196 $('.fa-spinner').remove(); 197 if (typeof Recaptcha !== "undefined" && Recaptcha.widget) { 198 Recaptcha.reload(); 199 } 200 // If the lightbox isn't visible, fix that 201 if(this.shown == false) { 202 $('#modal').modal('show'); 203 this.shown = true; 204 } 205 }, 206 207 /***********************************/ 208 /* ====== LIGHTBOX REQUESTS ====== */ 209 /***********************************/ 210 /** 211 * This function creates an XHR request to the URL 212 * and handles the response according to the callback. 213 * 214 * Default callback is changeContent 215 */ 216 getByUrl: function(url, post, callback) { 217 if(typeof callback == "undefined") { 218 // No custom handler: display return in lightbox 219 callback = this.changeContent; 220 } 221 // If the lightbox isn't visible, fix that 222 if(this.shown == false) { 223 $('#modal').modal('show'); 224 this.shown = true; 225 } 226 // Create our AJAX request, store it in case we need to cancel later 227 this.ajax({ 228 type:'POST', 229 url:url, 230 data:post, 231 success:function(html) { // Success! 232 callback(html); 233 }, 234 error:function(d,e) { 235 if (d.status == 200) { 236 try { 237 var data = JSON.parse(d.responseText); 238 Lightbox.changeContent('<p class="alert alert-danger">'+data.data+'</p>'); 239 } catch(error) { 240 Lightbox.changeContent('<p class="alert alert-danger">'+d.responseText+'</p>'); 241 } 242 } else if(d.status > 0) { 243 Lightbox.changeContent('<p class="alert alert-danger">'+d.statusText+' ('+d.status+')</p>'); 244 } 245 console.log(e,d); // Error reporting 246 console.log(url,post); 247 } 248 });
249 // Store current "page" context for empty targets 250 if(this.openingURL === false) { 251 this.openingURL = url; 252 } 253 this.lastURL = url; 254 this.lastPOST = post; 255 //this.openActions(); 256 return false; 257 }, 258 /** 259 * This is the friendly face to the function above. 260 * It converts a Controller and Action into a URL through the AJAX handler 261 * with GET and pushes the data and callback to the getByUrl 262 */ 263 get: function(controller, action, get, post, callback) { 264 // Build URL 265 var url = path+'/AJAX/JSON?method=getLightbox&submodule='+controller+'&subaction='+action; 266 if(typeof get !== "undefined" && get !== {}) { 267 url += '&'+$.param(get); 268 } 269 if(typeof post == "undefined") { 270 post = {}; 271 } 272 return this.getByUrl(url, post, callback); 273 }, 274 275 /**********************************/ 276 /* ====== FORM SUBMISSIONS ====== */ 277 /**********************************/ 278 /** 279 * Returns all the input values from a form as an associated array 280 * 281 * This function takes a jQuery wrapped form 282 * $(event.target) for example 283 */ 284 getFormData: function($form) { 285 // Gather all the data 286 var inputs = $form.find('*[name]'); 287 var data = {}; 288 for(var i=0;i<inputs.length;i++) { 289 var currentName = inputs[i].name; 290 var array = currentName.substring(currentName.length-2) == '[]'; 291 if(array && !data[currentName.substring(0,currentName.length-2)]) { 292 data[currentName.substring(0,currentName.length-2)] = []; 293 } 294 // Submit buttons 295 if(inputs[i].type == 'submit') { 296 if($(inputs[i]).attr('clicked') == 'true') { 297 data[currentName] = inputs[i].value; 298 } 299 // Radio buttons 300 } else if(inputs[i].type == 'radio') { 301 if(inputs[i].checked) { 302 if(array) { 303 var n = currentName.substring(0,currentName.length-2); 304 data[n].push(inputs[i].value); 305 } else { 306 data[currentName] = inputs[i].value; 307 } 308 } 309 // Checkboxes 310 } else if($(inputs[i]).attr('type') != 'checkbox' || inputs[i].checked) { 311 if(array) { 312 var f = currentName.substring(0,currentName.length-2); 313 data[f].push(inputs[i].value); 314 } else { 315 data[currentName] = inputs[i].value; 316 } 317 } 318 } 319 return data; 320 }, 321 /** 322 * This function adds submission events to forms loaded inside the lightbox 323 * 324 * First, it will check for custom handlers, for those who want to handle everything. 325 * 326 * Then, it will check for custom form callbacks. These will be added to an anonymous 327 * function that will call Lightbox.submit with the form and the callback. 328 * 329 * Finally, if nothing custom is setup, it will add the default function which 330 * calls Lightbox.submit with a callback to close if there are no errors to display. 331 * 332 * This is a default open action, so it runs every time changeContent 333 * is called and the 'shown' lightbox event is triggered 334 */ 335 registerForms: function() { 336 var $form = $("#modal").find('form'); 337 $form.validator(); 338 var name = $form.attr('name'); 339 // Assign form handler based on name 340 if(typeof name !== "undefined" && typeof Lightbox.formHandlers[name] !== "undefined") { 341 $form.submit(Lightbox.formHandlers[name]); 342 // Default action, with custom callback 343 } else if(typeof Lightbox.formCallbacks[name] !== "undefined") { 344 $form.submit(function(evt){ 345 if(evt.isDefaultPrevented()) { 346 $('.fa.fa-spinner', evt.target).remove(); 347 return false; 348 } 349 Lightbox.submit($(evt.target), Lightbox.formCallbacks[name]); 350 return false; 351 }); 352 // Default 353 } else { 354 $form.unbind('submit').submit(function(evt){ 355 if(evt.isDefaultPrevented()) { 356 $('.fa.fa-spinner', evt.target).remove(); 357 return false; 358 } 359 Lightbox.submit($(evt.target), function(html){ 360 Lightbox.checkForError(html, Lightbox.close); 361 }); 362 return false; 363 }); 364 } 365 }, 366 /** 367 * The default, automatic form submission 368 * 369 * This function gleans all the information in a form from the function above 370 * Then it uses the action="..." attribute of the form to figure out where to send the data 371 * and the method="" attribute to send it the proper way 372 * 373 * Forms without an action="..." are submitted to the current URL. 374 * In the case where we have a form with no action in the lightbox, 375 * we emulate that behaviour by submitting the last URL loaded through 376 * .getByUrl, stored in lastURL in the Lightbox object. 377 */ 378 submit: function($form, callback) { 379 // Default callback is to close 380 if(typeof callback == "undefined") { 381 callback = this.close; 382 } 383 var data = this.getFormData($form); 384 // If we have an action: parse 385 var POST = $form.attr('method') && $form.attr('method').toUpperCase() == 'POST'; 386 if($form.attr('action')) { 387 // Parse action location 388 var action = $form.attr('action').substring($form.attr('action').indexOf(path)+path.length+1); 389 var params = action.split('?'); 390 action = action.split('/'); 391 var get = params.length > 1 ? deparam(params[1]) : data['id'] ? {id:data['id']} : {}; 392 if(POST) { 393 this.get(action[0], action[action.length-1], get, data, callback); 394 } else { 395 this.get(action[0], action[action.length-1], data, {}, callback); 396 } 397 // If not: fake context by using the previous action 398 } else if(POST) { 399 this.getByUrl(this.lastURL, data, callback); 400 } else { 401 this.getByUrl(this.lastURL, {}, callback); 402 }
403 $(this).find('.modal-body').html(vufindString.loading + "..."); 404 } 405}; 406 407/** 408 * This is where you add click events to open the lightbox. 409 * We do it here so that non-JS users still have a good time. 410 */ 411$(document).ready(function() { 412 // Add handlers to the forms 413 Lightbox.addOpenAction(Lightbox.registerForms); 414 /** 415 * Hook into the Bootstrap close event 416 * 417 * Yes, the secret's out, our beloved Lightbox is a modal 418 */ 419 $('#modal').on('hidden.bs.modal', Lightbox.closeActions); 420 /** 421 * If a link with the class .modal-link triggers the lightbox, 422 * look for a title attribute to use as our lightbox title. 423 */ 424 $('.modal-link,.help-link').click(function() { 425 var title = $(this).attr('title'); 426 if(typeof title === "undefined") { 427 title = $(this).html(); 428 } 429 $('#modal .modal-title').html(title); 430 Lightbox.titleSet = true; 431 }); 432});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.