hjg
2023-11-18 bb48edb3d9faaaeab0088151c86fc24137acdb08
提交 | 用户 | 时间
58d006 1 /**
A 2  <b>Load content via Ajax </b>. For more information please refer to documentation #basics/ajax
3 */
4
5 (function($ , undefined) {
6     var ajax_loaded_scripts = {}
7
8     function AceAjax(contentArea, settings) {
9         var $contentArea = $(contentArea);
10         var self = this;
11         $contentArea.attr('data-ajax-content', 'true');
12         
13         //get a list of 'data-*' attributes that override 'defaults' and 'settings'
14         var attrib_values = ace.helper.getAttrSettings(contentArea, $.fn.ace_ajax.defaults);
15         this.settings = $.extend({}, $.fn.ace_ajax.defaults, settings, attrib_values);
16
17
18         var working = false;
19         var $overlay = $();//empty set
20
21         this.force_reload = false;//set jQuery ajax's cache option to 'false' to reload content
22         this.loadUrl = function(hash, cache, manual_trigger) {
23             var url = false;
24             hash = hash.replace(/^(\#\!)?\#/, '');
25             
26             this.force_reload = (cache === false)
27             
28             if(typeof this.settings.content_url === 'function') url = this.settings.content_url(hash);
29             if(typeof url === 'string') this.getUrl(url, hash, manual_trigger);
30         }
31         
32         this.loadAddr = function(url, hash, cache) {
33             this.force_reload = (cache === false);
34             this.getUrl(url, hash, false);
35         }
36
37
38         this.reload = function() {
39             var hash = $.trim(window.location.hash);
40             if(!hash && this.settings.default_url) hash = this.settings.default_url;
41             
42             this.loadUrl(hash, false);
43         }
44         this.post = function(url, data, updateView, extraParams) {
45             var url = url || $.trim(location.href.replace(location.hash,''));
46             if(!url) return;
47             var data = data || {}
48             var updateView = updateView || false;
49             this.getUrl(url, null, false, 'POST', data, updateView, extraParams);
50         }
51         
52         
53         this.getUrl = function(url, hash, manual_trigger, method, data, updateView, extraParams) {
54             if(working) {
55                 return;
56             }
57             
58             var method = method || 'GET';
59             var updateView = (method == 'GET') || (method == 'POST' && updateView == true)
60             var data = data || null;
61         
62             var event
63             $contentArea.trigger(event = $.Event('ajaxloadstart'), {url: url, hash: hash, method: method, data: data})
64             if (event.isDefaultPrevented()) return;
65             
66             self.startLoading();
67             
68             
69             var ajax_params = method == 'GET' ? {'url': url, 'cache': !this.force_reload} : {'url': url, 'method' : 'POST', 'data': data}
70             if(method == 'POST' && typeof extraParams == 'object') ajax_params = $.extend({}, ajax_params, extraParams);
71
72             $.ajax(ajax_params)
73             .error(function() {
74                 $contentArea.trigger('ajaxloaderror', {url: url, hash: hash, method: method, data: data});
75                 
76                 self.stopLoading(true);
77             })
78             .done(function(result) {
79                 $contentArea.trigger('ajaxloaddone', {url: url, hash: hash, method: method, data: data});
80                 if(method == 'POST') {
81                     var event
82                     $contentArea.trigger(event = $.Event('ajaxpostdone', {url: url, data: data, result: result}))
83                     if( event.isDefaultPrevented() ) updateView = false;
84                 }
85                 
86                 
87                 var link_element = null, link_text = '';
88                 if(typeof self.settings.update_active === 'function') {
89                     link_element = self.settings.update_active.call(null, hash, url, method, updateView);
90                 }
91                 else if(self.settings.update_active === true && hash) {
92                     link_element = $('a[data-url="'+hash+'"]');
93                     if(link_element.length > 0) {
94                         var nav = link_element.closest('.nav');
95                         if(nav.length > 0) {
96                             nav.find('.active').each(function(){
97                                 var $class = 'active';
98                                 if( $(this).hasClass('hover') || self.settings.close_active ) $class += ' open';
99                                 
100                                 $(this).removeClass($class);                            
101                                 if(self.settings.close_active) {
102                                     $(this).find(' > .submenu').css('display', '');
103                                 }
104                             })
105                             
106                             var active_li = link_element.closest('li').addClass('active').parents('.nav li').addClass('active open');
107                             nav.closest('.sidebar[data-sidebar-scroll=true]').each(function() {
108                                 var $this = $(this);
109                                 $this.ace_sidebar_scroll('reset');
110                                 if(manual_trigger == true) $this.ace_sidebar_scroll('scroll_to_active');//first time only
111                             })
112                         }
113                     }
114                 }
115
116                 /////////
117                 if(typeof self.settings.update_breadcrumbs === 'function') {
118                     link_text = self.settings.update_breadcrumbs.call(null, hash, url, link_element, method, updateView);
119                 }
120                 else if(self.settings.update_breadcrumbs === true && link_element != null && link_element.length > 0) {
121                     link_text = updateBreadcrumbs(link_element);
122                 }
123                 /////////
124                 
125                 $overlay.addClass('content-loaded').detach();
126                 if(updateView) {
127                     //convert "title" and "link" tags to "div" tags for later processing
128                     result = String(result)
129                         .replace(/<(title|link)([\s\>])/gi,'<div class="hidden ajax-append-$1"$2')
130                         .replace(/<\/(title|link)\>/gi,'</div>')
131                     $contentArea.empty().html(result);
132                 }
133                 
134                 $(self.settings.loading_overlay || $contentArea).append($overlay);
135
136
137
138                 //remove previous stylesheets inserted via ajax
139                 if(updateView) setTimeout(function() {
140                     $('head').find('link.ace-ajax-stylesheet').remove();
141
142                     var main_selectors = ['link.ace-main-stylesheet', 'link#main-ace-style', 'link[href*="/ace.min.css"]', 'link[href*="/ace.css"]']
143                     var ace_style = [];
144                     for(var m = 0; m < main_selectors.length; m++) {
145                         ace_style = $('head').find(main_selectors[m]).first();
146                         if(ace_style.length > 0) break;
147                     }
148                     
149                     $contentArea.find('.ajax-append-link').each(function(e) {
150                         var $link = $(this);
151                         if ( $link.attr('href') ) {
152                             var new_link = jQuery('<link />', {type : 'text/css', rel: 'stylesheet', 'class': 'ace-ajax-stylesheet'})
153                             if( ace_style.length > 0 ) new_link.insertBefore(ace_style);
154                             else new_link.appendTo('head');
155                             new_link.attr('href', $link.attr('href'));//we set "href" after insertion, for IE to work
156                         }
157                         $link.remove();
158                     })
159                 }, 10);
160
161                 //////////////////////
162
163                 if(typeof self.settings.update_title === 'function') {
164                     self.settings.update_title.call(null, hash, url, link_text, method, updateView);
165                 }
166                 else if(self.settings.update_title === true && method == 'GET') {
167                     updateTitle(link_text);
168                 }
169
170                 if( !manual_trigger && updateView ) {
171                     $('html,body').animate({scrollTop: 0}, 250);
172                 }
173
174                 //////////////////////
175                 $contentArea.trigger('ajaxloadcomplete', {url: url, hash: hash, method: method, data:data});
176                 //////////////////////
177                 
178                 
179                 //if result contains call to "loadScripts" then don't stopLoading now
180                 var re = /\.(?:\s*)ace(?:_a|A)jax(?:\s*)\((?:\s*)(?:\'|\")loadScripts(?:\'|\")/;
181                 if(result.match(re)) self.stopLoading();
182                 else self.stopLoading(true);
183             })
184         }
185         
186         
187         ///////////////////////
188         var fixPos = false;
189         var loadTimer = null;
190         this.startLoading = function() {
191             if(working) return;
192             working = true;
193             
194             if(!this.settings.loading_overlay && $contentArea.css('position') == 'static') {
195                 $contentArea.css('position', 'relative');//for correct icon positioning
196                 fixPos = true;
197             }
198                 
199             $overlay.remove();
200             $overlay = $('<div class="ajax-loading-overlay"><i class="ajax-loading-icon '+(this.settings.loading_icon || '')+'"></i> '+this.settings.loading_text+'</div>')
201
202             if(this.settings.loading_overlay == 'body') $('body').append($overlay.addClass('ajax-overlay-body'));
203             else if(this.settings.loading_overlay) $(this.settings.loading_overlay).append($overlay);
204             else $contentArea.append($overlay);
205
206             
207             if(this.settings.max_load_wait !== false) 
208              loadTimer = setTimeout(function() {
209                 loadTimer = null;
210                 if(!working) return;
211                 
212                 var event
213                 $contentArea.trigger(event = $.Event('ajaxloadlong'))
214                 if (event.isDefaultPrevented()) return;
215                 
216                 self.stopLoading(true);
217              }, this.settings.max_load_wait * 1000);
218         }
219         
220         this.stopLoading = function(stopNow) {
221             if(stopNow === true) {
222                 working = false;
223                 
224                 $overlay.remove();
225                 if(fixPos) {
226                     $contentArea.css('position', '');//restore previous 'position' value
227                     fixPos = false;
228                 }
229                 
230                 if(loadTimer != null) {
231                     clearTimeout(loadTimer);
232                     loadTimer = null;
233                 }
234             }
235             else {
236                 $overlay.addClass('almost-loaded');
237                 
238                 $contentArea.one('ajaxscriptsloaded.inner_call', function() {
239                     self.stopLoading(true);
240                     /**
241                     if(window.Pace && Pace.running == true) {
242                         Pace.off('done');
243                         Pace.once('done', function() { self.stopLoading(true) })
244                     }
245                     else self.stopLoading(true);
246                     */
247                 })
248             }
249         }
250         
251         this.working = function() {
252             return working;
253         }
254         ///////////////////////
255         
256         
257         
258         function updateBreadcrumbs(link_element) {
259             var link_text = '';
260          
261             //update breadcrumbs
262             var breadcrumbs = $('.breadcrumb');
263             if(breadcrumbs.length > 0 && breadcrumbs.is(':visible')) {
264                 breadcrumbs.find('> li:not(:first-child)').remove();
265
266                 var i = 0;        
267                 link_element.parents('.nav li').each(function() {
268                     var link = $(this).find('> a');
269                     
270                     var link_clone = link.clone();
271                     link_clone.find('i,.fa,.glyphicon,.ace-icon,.menu-icon,.badge,.label').remove();
272                     var text = link_clone.text();
273                     link_clone.remove();
274                     
275                     var href = link.attr('href');
276
277                     if(i == 0) {
278                         var li = $('<li class="active"></li>').appendTo(breadcrumbs);
279                         li.text(text);
280                         link_text = text;
281                     }
282                     else {
283                         var li = $('<li><a /></li>').insertAfter(breadcrumbs.find('> li:first-child'));
284                         li.find('a').attr('href', href).text(text);
285                     }
286                     i++;
287                 })
288             }
289             
290             return link_text;
291          }
292          
293          function updateTitle(link_text) {
294             var $title = $contentArea.find('.ajax-append-title');
295             if($title.length > 0) {
296                 document.title = $title.text();
297                 $title.remove();
298             }
299             else if(link_text.length > 0) {
300                 var extra = $.trim(String(document.title).replace(/^(.*)[\-]/, ''));//for example like " - Ace Admin"
301                 if(extra) extra = ' - ' + extra;
302                 link_text = $.trim(link_text) + extra;
303             }
304          }
305          
306          
307          this.loadScripts = function(scripts, callback) {
308             var scripts = scripts || [];
309             $.ajaxPrefilter('script', function(opts) {opts.cache = true});
310             setTimeout(function() {
311                 //let's keep a list of loaded scripts so that we don't load them more than once!
312                 
313                 function finishLoading() {
314                     if(typeof callback === 'function') callback();
315                     $('.btn-group[data-toggle="buttons"] > .btn').button();
316                     
317                     $contentArea.trigger('ajaxscriptsloaded');
318                 }
319                 
320                 //var deferreds = [];
321                 var deferred_count = 0;//deferreds count
322                 var resolved = 0;
323                 for(var i = 0; i < scripts.length; i++) if(scripts[i]) {
324                     (function() {
325                         var script_name = "js-"+scripts[i].replace(/[^\w\d\-]/g, '-').replace(/\-\-/g, '-');
326                         if( ajax_loaded_scripts[script_name] !== true )    deferred_count++;
327                     })()
328                 }
329                 
330
331                 function nextScript(index) {
332                     index += 1;
333                     if(index < scripts.length) loadScript(index);
334                     else {
335                         finishLoading();
336                     }
337                 }
338                 
339                 function loadScript(index) {
340                     index = index || 0;
341                     if(!scripts[index]) {//could be null sometimes
342                         return nextScript(index);
343                     }
344                 
345                     var script_name = "js-"+scripts[index].replace(/[^\w\d\-]/g, '-').replace(/\-\-/g, '-');
346                     //only load scripts that are not loaded yet!
347                     if( ajax_loaded_scripts[script_name] !== true ) {
348                         $.getScript(scripts[index])
349                         .done(function() {
350                             ajax_loaded_scripts[script_name] = true;
351                         })
352                         //.fail(function() {
353                         //})
354                         .complete(function() {
355                             resolved++;
356                             if(resolved >= deferred_count && working) {
357                                 finishLoading();
358                             }
359                             else {
360                                 nextScript(index);
361                             }
362                         })
363                     }
364                     else {//script previoisly loaded
365                         nextScript(index);
366                     }
367                 }
368                 
369                 
370                 if (deferred_count > 0) {
371                     loadScript();
372                 }
373                 else {
374                     finishLoading();
375                 }
376
377             }, 10)
378         }
379         
380         
381         
382         /////////////////
383         $(window)
384         .off('hashchange.ace_ajax')
385         .on('hashchange.ace_ajax', function(e, manual_trigger) {
386             var hash = $.trim(window.location.hash);
387             if(!hash || hash.length == 0) return;
388             
389             if(self.settings.close_mobile_menu) {
390                 try {$(self.settings.close_mobile_menu).ace_sidebar('mobileHide')} catch(e){}
391             }
392             if(self.settings.close_dropdowns) {
393                 $('.dropdown.open .dropdown-toggle').dropdown('toggle');
394             }
395             
396             self.loadUrl(hash, null, manual_trigger);
397         }).trigger('hashchange.ace_ajax', [true]);
398         
399         var hash = $.trim(window.location.hash);
400         if(!hash && this.settings.default_url) window.location.hash = this.settings.default_url;
401
402     }//AceAjax
403
404
405
406     $.fn.aceAjax = $.fn.ace_ajax = function (option, value, value2, value3, value4) {
407         var method_call;
408
409         var $set = this.each(function () {
410             var $this = $(this);
411             var data = $this.data('ace_ajax');
412             var options = typeof option === 'object' && option;
413
414             if (!data) $this.data('ace_ajax', (data = new AceAjax(this, options)));
415             if (typeof option === 'string' && typeof data[option] === 'function') {
416                 if(value4 !== undefined) method_call = data[option](value, value2, value3, value4);
417                 else if(value3 !== undefined) method_call = data[option](value, value2, value3);
418                 else if(value2 !== undefined) method_call = data[option](value, value2);
419                 else method_call = data[option](value);
420             }
421         });
422
423         return (method_call === undefined) ? $set : method_call;
424     }
425     
426     
427     
428     $.fn.aceAjax.defaults = $.fn.ace_ajax.defaults = {
429         content_url: false,
430         default_url: false,
431         loading_icon: 'fa fa-spin fa-spinner fa-2x orange',
432         loading_text: '',
433         loading_overlay: null,
434         update_breadcrumbs: true,
435         update_title: true,
436         update_active: true,
437         close_active: false,
438         max_load_wait: false,
439         close_mobile_menu: false,
440         close_dropdowns: false
441      }
442
443 })(window.jQuery);
444