WinJS does not show the autosuggestbox - javascript

I have a problem with WinJS. I don't get the problem. I want to fill (and I do it) the autosuggestbox with data-items. After that, normally, it has to show the autosuggestbox. But WinJS does not draw the list.
HomeController:
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/home/home.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app’s data.
ready: function (element, options) {
var searchBox1 = document.querySelector("#station1");
var searchBox2 = document.querySelector("#station2");
searchBox1.addEventListener("suggestionsrequested", GuiEvents.stationRequest);
searchBox1.addEventListener("querysubmitted", Main.stationSubmittedHandler);
searchBox2.addEventListener("suggestionsrequested", GuiEvents.stationRequest);
searchBox2.addEventListener("querysubmitted", Main.stationSubmittedHandler);
WinJS.UI.processAll();
}
});
})();
The eventhandler:
static stationRequest(event: CustomEvent) {
var query = <string>event.detail.queryText;
var suggestionCollection = event.detail.searchSuggestionCollection;
var callback = function (response) {
event.detail.setPromise(WinJS.Promise.then(null, function () {
var _locations: Array<Object> = JSON.parse(response.responseText).LocationList.StopLocation;
$("#response").text(JSON.stringify(_locations));
_locations.forEach(function (obj: ILocation, index, _loc) {
suggestionCollection.appendQuerySuggestion(obj.name);
});
event.detail.linguisticDetails.queryTextAlternatives.forEach(
function (element, index, array) {
if (element.substr(0, query.length).toLocaleLowerCase() === query) {
suggestionCollection.appendQuerySuggestion(element);
}
});
}));
};
if (query.length > 3) {
API.stationRequest(query, callback);
}
}
I copied more or less the example form the windows WinJS site, but... It shows the responsetext very well to me in the $("#response")-element, but I get no box or list.
What is the failure.

Related

'No Data' view getting opens first and then Detail Page opens with the data in Fiori

I am developing a Master Detail application in which if the service URL doesn't return data, then a view called 'NoData' should open. But what actually is happening that first, the 'NoData' view opens and then the Detail Page with the data gets displayed. I don't know why and how that 'NoData' page is appearing first. Below is my code for Master Page :
Controller.js :
onInit: function () {
this.router = sap.ui.core.UIComponent.getRouterFor(this);
this._custTemp = this.getView().byId("listItemTemp").clone();
this.refreshFlag = true; // Flag to get new data or not for customers
this.totalModel = sap.ui.getCore().getModel("totalModel");
this.getView().setModel(this.totalModel, "totalModel");
this.oDataModel = sap.ui.getCore().getModel("DataModel");
this.getView().setModel(this.oDataModel, "DataModel");
this.oInitialLoadFinishedDeferred = jQuery.Deferred();
var oEventBus = sap.ui.getCore().getEventBus();
this.getView().byId("listId").attachEvent("updateFinished", function () {
this.oInitialLoadFinishedDeferred.resolve();
oEventBus.publish("MasterPage", "InitialLoadFinished", {
oListItem: this.getView().byId("listId").getItems()[0]
});
if (!sap.ui.Device.system.phone) {
this._getFirstItem();
}
}, this);
this.functionData = [];
},
waitForInitialListLoading: function (fnToExecute) {
jQuery.when(this.oInitialLoadFinishedDeferred).then(jQuery.proxy(fnToExecute, this));
},
_getFirstItem: function () {
sap.ui.core.BusyIndicator.show();
this.waitForInitialListLoading(function () {
// On the empty hash select the first item
var list = this.getView().byId("listId");
var selectedItem = list.getItems()[0];
if (selectedItem) {
list.setSelectedItem(selectedItem, true);
var data = list.getBinding("items").getContexts()[0];
sap.ui.getCore().getModel("detailModel").setData(data.getObject());
this.router.navTo('DetailPage', {
QueryNo: data.EICNO
});
sap.ui.core.BusyIndicator.hide();
} else {
this.router.navTo('NoData');
}
}, this);
},
onBeforeRendering: function () {
this._fnGetData();
},
_fnGetData: function (oEvent) {
var that = this;
this.getView().setModel(this.totalModel, "totalModel");
if (this.refreshFlag === true) {
sap.ui.core.BusyIndicator.show(0);
$.ajax({
url: "/sap/opu/odata/sap/ZHR_V_CARE_SRV/EmpQueryInitSet('10002001')?$expand=QueryLoginToQueryList/QueryToLog",
method: "GET",
dataType: "json",
success: function (data) {
that.getView().getModel("totalModel").setData(data.d.QueryLoginToQueryList);
that.refreshFlag = false;
sap.ui.core.BusyIndicator.hide();
that.statusList();
},
error: function (err) {
sap.ui.core.BusyIndicator.hide();
MessageBox.information(err.responseText + "Please try again");
}
});
}
}
totalModel is a json model, right? You'll get two updateFinished events on app load. The first one is triggered once the list control is rendered and binding is done (when the model has no data), and the second comes after your $.ajax call updates data to totalModel.
I think you can solve it by moving your NoData navigation to both 'success' and 'error' callbacks of your $.ajax call. Doing so may cover other use cases e.g. if you are using URL navigation parameters and a user changes the entity ID in the URL to some random number, it'd navigate to your NoDatapage.

jQuery plugin instances variable with event handlers

I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.

Update event listeners when page switches

I'm in a bit of a predicament, because I need to somehow update event listeners, when the page changes using ajax. I have a specific element that I need to update based on what page the ajax call injects. Here's my issue:
I have this slider control constructor:
UI.CONTROLS.SLIDER = function (select, action, actionWhenActive, actionWhenSet) {
'use strict';
var self = this;
this.action = action;
this.select = select;
this.area = select.find($('area-'));
this.fill = select.find($('fill-'));
this.value = 0;
this.active = false;
this.actionWhenActive = actionWhenActive;
this.actionWhenSet = actionWhenSet;
function eventlisteners(self) {
$(document).on('mousemove', function (event) {
self.move(event, self);
});
$(document).on('mouseup', function (event) {
self.drop(event, self);
});
self.area.on('mousedown', function (event) {
self.grab(event, self);
});
}
eventlisteners(self);
this.reselect = function (element) {
self.area = element.find($('area-'));
self.fill = element.find($('fill-'));
eventlisteners(self);
};
};
UI.CONTROLS.SLIDER.prototype = {
action: this.action,
width: function () {
'use strict';
var calcWidth = ((this.value * 100) + '%');
this.fill.width(calcWidth);
},
update: function (event, self) {
'use strict';
if (this.actionWhenActive === true) {
this.action();
}
var direction, percent, container, area;
direction = event.pageX - this.area.offset().left;
percent = Math.min(Math.max(direction / this.area.width(), 0), 1.0);
this.value = percent;
this.width();
},
move: function (event, self) {
'use strict';
if (this.active === true) {
this.update(event);
}
},
grab: function (event, self) {
'use strict';
this.active = true;
self.update(event);
event.preventDefault();
},
drop: function (event, self) {
'use strict';
if (this.active === true) {
this.active = false;
this.action();
}
},
setValue: function (value) {
'use strict';
if (this.active === false) {
this.value = value;
this.width();
if (this.actionWhenSet === true) {
this.action();
}
}
}
};
This can create new sliders based on the container (select) specified. In my website, I have an audio player. So using ajax you can navigate while this audio player plays. I have two states, viewing a track, and not viewing a track. When you're not viewing the track that is playing, a transport bar will pop down from the header containing the scrubber (slider control), this scrubber is also inside the track view (viewing the track) page.
This code checks if you're viewing the track that is playing. audioFromView gets updated on the ajax calls, it basically replaces it with what track you're viewing. It then compares it with audioCurrent which is the track currently playing, UI.PAGE.TYPE is what type of page you're viewing, in this instance a track:
var audioViewIsCurrent = function () {
'use strict';
if (audioCurrent.src === audioFromView.src && UI.PAGE.TYPE === 'track') {
return true;
} else {
return false;
}
};
So this code then updates the scrubber based on the above code's output (audioElementTrackView is the scrubber inside the track page, and audioElementTransport is the scrubber inside the transport panel):
var audioScrubber = {
element: {},
active: false,
action: function () {
'use strict';
audioScrubber.active = this.active;
var time = audioSource.duration * this.value;
if (this.active === false) {
audioCurrent.time(this.value * duration);
}
},
set: function () {
'use strict';
var container, slider;
if (audioElementTrackView.length === 1) {
container = audioElementTrackView.find(audioElementScrubber);
} else {
container = audioElementTransport.find(audioElementScrubber);
}
this.element = new UI.CONTROLS.SLIDER(container, this.action, true);
},
reselect: function () {
if (audioElementTrackView.length === 1) {
container = audioElementTrackView.find(audioElementScrubber);
} else {
container = audioElementTransport.find(audioElementScrubber);
}
this.element.reselect(container)
}
};
audioScrubber.reselect()
So this works fine with how I'm currently doing it, HOWEVER since I am adding new event listeners everytime I update my scrubber object (inside the ajax call) in order to keep the scrubber working I am also piling them up, making the old event listeners take up space and RAM eventually making the site slow down to a halt (if you navigate enough)
I tested this using console.log on mouseup, everytime I switched page, it would log it twice, and then thrice and so on.
How can I avoid this?
You can delegate events to the nearest static ancestor, so you don't need to rebind them everyime.
Read this article
https://davidwalsh.name/event-delegate

AngularJS : How to run JavaScript from inside Directive after directive is compiled and linked

I have a responsive template that I am trying to use with my Angularjs app. This is also my first Angular app so I know I have many mistakes and re-factoring in my future.
I have read enough about angular that I know DOM manipulations are suppose to go inside a directive.
I have a javascript object responsible for template re-sizes the side menu and basically the outer shell of the template. I moved all of this code into a directive and named it responsive-theme.
First I added all the methods that are being used and then I defined the App object at the bottom. I removed the function bodies to shorten the code.
Basically the object at the bottom is a helper object to use with all the methods.
var directive = angular.module('bac.directive-manager');
directive.directive('responsiveTheme', function() {
return {
restrict: "A",
link: function($scope, element, attrs) {
// IE mode
var isRTL = false;
var isIE8 = false;
var isIE9 = false;
var isIE10 = false;
var sidebarWidth = 225;
var sidebarCollapsedWidth = 35;
var responsiveHandlers = [];
// theme layout color set
var layoutColorCodes = {
};
// last popep popover
var lastPopedPopover;
var handleInit = function() {
};
var handleDesktopTabletContents = function () {
};
var handleSidebarState = function () {
};
var runResponsiveHandlers = function () {
};
var handleResponsive = function () {
};
var handleResponsiveOnInit = function () {
};
var handleResponsiveOnResize = function () {
};
var handleSidebarAndContentHeight = function () {
};
var handleSidebarMenu = function () {
};
var _calculateFixedSidebarViewportHeight = function () {
};
var handleFixedSidebar = function () {
};
var handleFixedSidebarHoverable = function () {
};
var handleSidebarToggler = function () {
};
var handleHorizontalMenu = function () {
};
var handleGoTop = function () {
};
var handlePortletTools = function () {
};
var handleUniform = function () {
};
var handleAccordions = function () {
};
var handleTabs = function () {
};
var handleScrollers = function () {
};
var handleTooltips = function () {
};
var handleDropdowns = function () {
};
var handleModal = function () {
};
var handlePopovers = function () {
};
var handleChoosenSelect = function () {
};
var handleFancybox = function () {
};
var handleTheme = function () {
};
var handleFixInputPlaceholderForIE = function () {
};
var handleFullScreenMode = function() {
};
$scope.App = {
//main function to initiate template pages
init: function () {
//IMPORTANT!!!: Do not modify the core handlers call order.
//core handlers
handleInit();
handleResponsiveOnResize(); // set and handle responsive
handleUniform();
handleScrollers(); // handles slim scrolling contents
handleResponsiveOnInit(); // handler responsive elements on page load
//layout handlers
handleFixedSidebar(); // handles fixed sidebar menu
handleFixedSidebarHoverable(); // handles fixed sidebar on hover effect
handleSidebarMenu(); // handles main menu
handleHorizontalMenu(); // handles horizontal menu
handleSidebarToggler(); // handles sidebar hide/show
handleFixInputPlaceholderForIE(); // fixes/enables html5 placeholder attribute for IE9, IE8
handleGoTop(); //handles scroll to top functionality in the footer
handleTheme(); // handles style customer tool
//ui component handlers
handlePortletTools(); // handles portlet action bar functionality(refresh, configure, toggle, remove)
handleDropdowns(); // handle dropdowns
handleTabs(); // handle tabs
handleTooltips(); // handle bootstrap tooltips
handlePopovers(); // handles bootstrap popovers
handleAccordions(); //handles accordions
handleChoosenSelect(); // handles bootstrap chosen dropdowns
handleModal();
$scope.App.addResponsiveHandler(handleChoosenSelect); // reinitiate chosen dropdown on main content resize. disable this line if you don't really use chosen dropdowns.
handleFullScreenMode(); // handles full screen
},
fixContentHeight: function () {
handleSidebarAndContentHeight();
},
setLastPopedPopover: function (el) {
lastPopedPopover = el;
},
addResponsiveHandler: function (func) {
responsiveHandlers.push(func);
},
// useful function to make equal height for contacts stand side by side
setEqualHeight: function (els) {
var tallestEl = 0;
els = jQuery(els);
els.each(function () {
var currentHeight = $(this).height();
if (currentHeight > tallestEl) {
tallestColumn = currentHeight;
}
});
els.height(tallestEl);
},
// wrapper function to scroll to an element
scrollTo: function (el, offeset) {
pos = el ? el.offset().top : 0;
jQuery('html,body').animate({
scrollTop: pos + (offeset ? offeset : 0)
}, 'slow');
},
scrollTop: function () {
App.scrollTo();
},
// wrapper function to block element(indicate loading)
blockUI: function (ele, centerY) {
var el = jQuery(ele);
el.block({
message: '<img src="./assets/img/ajax-loading.gif" align="">',
centerY: centerY !== undefined ? centerY : true,
css: {
top: '10%',
border: 'none',
padding: '2px',
backgroundColor: 'none'
},
overlayCSS: {
backgroundColor: '#000',
opacity: 0.05,
cursor: 'wait'
}
});
},
// wrapper function to un-block element(finish loading)
unblockUI: function (el) {
jQuery(el).unblock({
onUnblock: function () {
jQuery(el).removeAttr("style");
}
});
},
// initializes uniform elements
initUniform: function (els) {
if (els) {
jQuery(els).each(function () {
if ($(this).parents(".checker").size() === 0) {
$(this).show();
$(this).uniform();
}
});
} else {
handleUniform();
}
},
updateUniform : function(els) {
$.uniform.update(els);
},
// initializes choosen dropdowns
initChosenSelect: function (els) {
$(els).chosen({
allow_single_deselect: true
});
},
initFancybox: function () {
handleFancybox();
},
getActualVal: function (ele) {
var el = jQuery(ele);
if (el.val() === el.attr("placeholder")) {
return "";
}
return el.val();
},
getURLParameter: function (paramName) {
var searchString = window.location.search.substring(1),
i, val, params = searchString.split("&");
for (i = 0; i < params.length; i++) {
val = params[i].split("=");
if (val[0] == paramName) {
return unescape(val[1]);
}
}
return null;
},
// check for device touch support
isTouchDevice: function () {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
},
isIE8: function () {
return isIE8;
},
isRTL: function () {
return isRTL;
},
getLayoutColorCode: function (name) {
if (layoutColorCodes[name]) {
return layoutColorCodes[name];
} else {
return '';
}
}
};
}
};
});
Originally the App.init() object method would be called at the bottom of any regular html page, and I have others that do certain things also that would be used on specific pages like Login.init() for the login page and so forth.
I did read that stackoverflow post
"Thinking in AngularJS" if I have a jQuery background? and realize that I am trying to go backwards in a sense, but I want to use this template that I have so I need to retro fit this solution.
I am trying to use this directive on my body tag.
<body ui-view="dashboard-shell" responsive-theme>
<div class="page-container">
<div class="page-sidebar nav-collapse collapse" ng-controller="SidemenuController">
<sidemenu></sidemenu>
</div>
<div class="page-content" ui-view="dashboard">
</div>
</div>
</body>
So here is my problem. This kinda sorta works. I don't get any console errors but when I try to use my side menu which the javascript for it is in the directive it doesn't work until I go inside the console and type App.init(). After that all of the template javascript works. I want to know how to do responsive theme stuff in these directives. I have tried using it both in the compile and link sections. I have tried putting the code in compile and link and calling the $scope.App.init() from a controller and also at the bottom after defining everything. I also tried putting this in jsfiddle but can't show a true example without having the console to call App.init().
My end design would be having some way to switch the pages through ui-router and when a route gets switched it calls the appropriate methods or re-runs the directive or something. The only method that will run on every page is the App.init() method and everything else is really page specific. And technically since this is a single page app the App.init() only needs to run once for the application. I have it tied to a parent template inside ui-router and the pages that will switch all use this shell template. There are some objects that need to access other to call their methods.
Im sorry in advance for maybe a confusing post. I am struggling right now trying to put together some of the ways that you do things from an angular perspective. I will continue to edit the post as I get responses to give further examples.
You said I have read enough about angular that I know DOM manipulations are suppose to go inside a directive but it sounds like you missed the point of a directive. A directive should handle DOM manipulation, yes, but not one directive for the entire page. Each element (or segment) of the page should have its own directive (assuming DOM manip needs to be done on that element) and then the $controller should handle the interactions between those elements and your data (or model).
You've created one gigantic directive and are trying to have it do way too much. Thankfully, you've kinda sorta designed your code in such a way that it shouldn't be too hard to break it up into several directives. Basically, each of your handle functions should be its own directive.
So you'd have something like:
.directive('sidebarMenu', function(){
return {
template: 'path/to/sidebar/partial.html',
link: function(scope, elem, attrs){
// insert the code for your 'handleSidebarMenu()' function here
}
};
})
.directive('horizontalMenu', function(){
return {
template: 'path/to/horizontal/partial.html',
link: function(scope, elem, attrs){
// insert the code for your 'handleHorizontalMenu()' function here
}
};
})
and then your view would look something like:
<body ui-view="dashboard-shell" responsive-theme>
<div class="page-container">
<div class="page-sidebar nav-collapse collapse">
<horizontal-menu></horizontal-menu>
<sidebar-menu></sidebar-menu>
</div>
<div class="page-content" ui-view="dashboard">
</div>
</div>
</body>
And then you don't need a SidebarmenuController because your controller functions shouldn't be handling DOM elements like the sidebar. The controller should just handling the data that you're going to display in your view, and then the view (or .html file) will handle the displaying and manipulation of that data by its use of the directives you've written.
Does that make sense? Just try breaking that huge directive up into many smaller directives that handle specific elements or specific tasks in the DOM.

knockoutjs data saving UI issue

In my web app while I am updating some data I need to show some loading spinning gif in the web page.
This is my code.
This is my html code
<img src="../../../../Content/images/submit-gif.gif" class="hidegif" data-bind="visible: isWaiting"/>
<button data-bind="click: createNew">Save</button>
In my knockoutjs model I have this
self.isWaiting = ko.observable(false);
self.createNew = function () {
this.isWaiting(true);
$.getJSON("/Admin/Material/GetFolders", function (allData) {
this.isWaiting(true);
var mappedFolders = $.map(allData, function (item) { return new Folder(item); });
self.folders(mappedFolders);
this.isWaiting(false);
}).success(function () { this.isWaiting(false); }).error(function () { }).complete(function () { this.isWaiting(false); }); ;
};
I have property called isWaiting. Before I call the server I am setting it to true. In completion and successive method I am setting it back to false.
So based on that my spinning wheel should appear and disappear.
But this is not working.
Thanks In Advance
this will have another context inside the createNew and callback functions. You should use self instead of this for accessing view model's property:
self.createNew = function () {
self.isWaiting(true);
$.getJSON("/Admin/Material/GetFolders", function (allData) {
self.isWaiting(true);
var mappedFolders = $.map(allData, function (item) { return new Folder(item); });
self.folders(mappedFolders);
self.isWaiting(false);
}).success(function () {
self.isWaiting(false);
}).error(function () {})
.complete(function () {
self.isWaiting(false);
});
};

Categories

Resources