-
-
Save draeton/4285da1f539cc00d7eed to your computer and use it in GitHub Desktop.
Ford » My Saved Items code sample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery,ngbs,NextGen,MySavedItems*/ | |
/** | |
* MySavedItems.Data | |
* | |
* Copyright (c) 2012, Team Detroit | |
* All rights reserved | |
* | |
* @desc Retrieves and process the My Saved Items data | |
* @dependencies jQuery + .views, .render, .observable | |
*/ | |
(function (window, $, ngbs, NextGen, MySavedItems) { | |
"use strict"; | |
var console = window.console || { | |
log: function () {} | |
}; | |
$.extend(MySavedItems.Events, { | |
"DATA_REQUEST_START": "DATA_REQUEST_START", | |
"DATA_AXZ_ERROR": "DATA_AXZ_ERROR" | |
}); | |
var NAMESPACE = ".MSIDATA"; | |
/* shortcuts */ | |
var $MSI = $(MySavedItems); | |
var E = MySavedItems.Events; | |
var P = MySavedItems.Page; | |
var NG = NextGen; | |
var $NG = $(NG); | |
var NGAW = NG.AuthenticationWidget; | |
var NGSAPI = NG.SaveAPI; | |
/** | |
* MySavedItems Data module | |
*/ | |
var D = MySavedItems.Data = (function () { | |
/* public interface */ | |
return { | |
/** | |
* init | |
* | |
* Sets up the element for rendering | |
* | |
* @param {jQuery} selector References the element where view is created | |
*/ | |
init: function ($element, options) { | |
var $widget = $("#nextgen_auth_save_widget_container"); | |
var baseURL = $widget.data("baseUrl"); | |
var make = $widget.data("make"); | |
NextGen.init({ | |
baseURL: baseURL, | |
make: make | |
}); | |
/* bind handlers */ | |
D._unbindHandlers(); | |
D._bindHandlers(); | |
/* trigger data retrieval */ | |
D._requestSavedItems(); | |
}, | |
/** | |
* _unbindHandlers | |
*/ | |
_unbindHandlers: function () { | |
$MSI.unbind(NAMESPACE); | |
$NG.unbind(NAMESPACE); | |
}, | |
/** | |
* _bindHandlers | |
* | |
* Bind handlers to msi data events | |
*/ | |
_bindHandlers: function () { | |
$MSI.bind(E.DATA_REQUEST_START + NAMESPACE, D._requestSavedItems); | |
$MSI.bind(E.DATA_AXZ_ERROR + NAMESPACE, P._axzAuthFlip); | |
$NG.bind(NG.events.AUTHENTICATION_COMPLETE_EVENT + NAMESPACE, D._requestSavedItems); | |
$NG.bind(NG.events.AUTHENTICATION_LOGOUT_EVENT + NAMESPACE, D._requestSavedItems); | |
}, | |
/** | |
* _requestSavedItems | |
* | |
* Request data from API | |
* | |
* @param {Object} e jQuery event | |
*/ | |
_requestSavedItems: function (e) { | |
var success = D._requestSavedItemsSuccess; | |
var error = D._requestSavedItemsError; | |
MySavedItems.init(); | |
$MSI.trigger(E.PAGE_RENDER_RESET); | |
if (NGAW.userId !== null) { | |
NGSAPI.getSavedItems(success, error); | |
} else { | |
D._requestSavedItemsError(); | |
} | |
}, | |
/** | |
* _requestSavedItemsSuccess | |
* | |
* Handles the JSON response data | |
* | |
* @param {Object} data | |
*/ | |
_requestSavedItemsSuccess: function (data, status, jqXHR) { | |
var response = data.Response; | |
if (response && response.status === "OK") { | |
D._processSavedItems(response.SavedItems); | |
} else { | |
$MSI.trigger(E.PAGE_RENDER_START); | |
} | |
}, | |
/** | |
* _requestSavedItemsError | |
* | |
* Handles AJAX error | |
*/ | |
_requestSavedItemsError: function () { | |
D.__processed = { | |
total: 0, | |
userID: NGAW.userId, | |
provider: NGAW.provider | |
}; | |
$MSI.trigger(E.PAGE_RENDER_START, D.__processed); | |
}, | |
/** | |
* _processSavedItems | |
* | |
* Pre-process saved items JSON in preparation for templates | |
* @param {Object} data API JSON data | |
*/ | |
_processSavedItems: function (data) { | |
/* set up buckets */ | |
D.__items = []; | |
D.__models = {}; | |
D.__vehicles = []; | |
D.__vehiclesUpdates = []; | |
D.__vehiclesUpdatesArray = []; | |
D.__technology = []; | |
D.__recent = null; | |
D.__totalVehicles = 0; | |
/* set axz plan type before processing */ | |
D.planType = D._getPlanType(); | |
/* parse out the data nodes into lists by data type */ | |
D.__lists = { | |
cpo: data.CPOVehicles && data.CPOVehicles.CPOVehicle, | |
config: data.ConfigVehicles && data.ConfigVehicles.ConfigVehicle, | |
inventory: data.InventoryVehicles && data.InventoryVehicles.InventoryVehicle, | |
compare: data.VehicleCompareItems && data.VehicleCompareItems.VehicleCompare, | |
gallery: data.BrandContents && data.BrandContents.BrandContent | |
}; | |
/* begin processing */ | |
$.each(D.__lists, D._sortDataTypeListsIntoItems); | |
$.each(D.__items, D._formatDate); | |
/* sort and set recent */ | |
D.__items = D.__items.sort(function (a, b) { | |
return a.CreationDateFull === b.CreationDateFull ? 0 : a.CreationDateFull > b.CreationDateFull ? -1 : 1; | |
}); | |
D.__recent = D.__items[0]; | |
/* continue processing */ | |
$.each(D.__items, D._setItemDisplayPricing); | |
/* processing data for Get Updates Flip */ | |
D.__vehiclesUpdates = D.__items.sort(function (a, b) { | |
return a.Year === b.Year ? 0 : a.Year > b.Year ? -1 : 1; | |
}); | |
$.each(D.__vehiclesUpdates, D._sortModelsIntoArrayForUpdates); | |
$.each(D.__items, D._sortItemsIntoArraysByModel); | |
/* if there are no vehicles, create an empty vehicle */ | |
if (!D.__totalVehicles) { | |
D.__models.empty = { | |
cpo: [], | |
config: [], | |
inventory: [], | |
compare: [], | |
gallery: [] | |
}; | |
} | |
$.each(D.__models, D._sortModelsIntoArraysByVehicle); | |
/* alpha sort vehicles for display */ | |
D.__vehicles = D.__vehicles.sort(function (a, b) { | |
return a.model === b.model ? 0 : a.model < b.model ? -1 : 1; | |
}); | |
/* returned object */ | |
D.__processed = { | |
recent: D.__recent, | |
vehicles: D.__vehicles, | |
technology: D.__technology, | |
firstVehicle: D.__vehicles[0], | |
totalVehicles: D.__totalVehicles, | |
total: D.__items.length, | |
userID: NGAW.userId, | |
provider: NGAW.provider | |
}; | |
/* trigger rendering */ | |
$MSI.trigger(E.PAGE_RENDER_START, D.__processed); | |
}, | |
/** | |
* _sortDataTypeListsIntoItems | |
* | |
* Push items into a single items array while adding properties | |
*/ | |
_sortDataTypeListsIntoItems: function (dataType, list) { | |
/* convert single objects to an array */ | |
list = $.isPlainObject(list) ? [list] : list; | |
$.each(list, function (index, item) { | |
item.dataType = dataType; | |
/* skip these keys when decoding */ | |
var skip = ["Pricing", "CreationDate", "PivotVehicle", "CompetitorVehicles"]; | |
/* decode values */ | |
$.each(item, function (key, value) { | |
if ($.inArray(key, skip) === -1) { | |
item[key] = decodeURIComponent(value); | |
} | |
}); | |
/* remove old title from description */ | |
if (item.Description) { | |
/*jslint regexp:true*/ | |
/*item.Description = item.Description.replace(/^.+\|\|/, "");*/ | |
item.Description = item.Description.split("||"); | |
item.OriginalTitle = item.Description[0]; | |
item.Description = item.Description[1]; | |
} | |
/* add to items array */ | |
D.__items.push(item); | |
}); | |
}, | |
/** | |
* _formatDate | |
*/ | |
_formatDate: (function () { | |
var dateRegex = /(\d{4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2}) (\w{2})/; | |
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; | |
var formatDate = function (d) { | |
return months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear(); | |
}; | |
// e.g. 2012-2-23 5:2 AM | |
return function (index, item) { | |
var parts; | |
var meridian; | |
var dateTime; | |
if (!item.CreationDateFull) { | |
parts = item.CreationDate.match(dateRegex); | |
meridian = parts[7]; | |
if (meridian === "PM") { | |
parts[4] = +parts[4] + 12; | |
} | |
dateTime = new Date(parts[1], parts[2] - 1, parts[3], parts[4], parts[5], parts[6]); | |
item.CreationDate = formatDate(dateTime); | |
item.CreationDateFull = dateTime; | |
} | |
}; | |
}()), | |
/** | |
* _setItemDisplayPricing | |
* | |
* Select the correct pricing to display based on plan type | |
*/ | |
_setItemDisplayPricing: (function () { | |
/* see: http://stackoverflow.com/a/149099 */ | |
var formatMoney = function (n, c, d, t) { | |
var s, i, j; | |
c = Math.abs(c); | |
c = isNaN(c) ? 2 : c; | |
d = typeof d === "undefined" ? "." : d; | |
t = typeof t === "undefined" ? "," : t; | |
s = n < 0 ? "-" : ""; | |
n = Math.abs(+n || 0).toFixed(c); | |
i = parseInt(n, 10).toString(); | |
j = i.length; | |
j = (j) > 3 ? j % 3 : 0; | |
return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); | |
}; | |
return function (index, item) { | |
var flagged = false; | |
if (item.Pricing) { | |
/* format prices as currency */ | |
$.each(item.Pricing, function (index, price) { | |
item.Pricing[index] = formatMoney(price, 0); | |
}); | |
/* set default plan type */ | |
if (!item.PlanType) { | |
item.PlanType = "MSRP"; | |
} | |
/* AXZ: check that user can view data */ | |
if (item.PlanType !== "MSRP") { | |
if (D.planType === "MSRP") { | |
flagged = true; | |
} else { | |
if (D.planType !== item.PlanType) { | |
flagged = true; | |
} | |
} | |
} | |
/* AXZ: user is only shown MSRP with an option to login */ | |
if (flagged) { | |
$MSI.trigger(E.DATA_AXZ_ERROR, item.PlanType); | |
item.PlanType = "MSRP"; | |
} | |
/* AXZ: choose display pricing */ | |
switch (item.PlanType) { | |
case "MSRP": | |
item.DisplayPricing = { | |
BasePrice: item.Pricing.BaseMSRP, | |
Options: item.Pricing.Options, | |
Destination: item.Pricing.DestinationDeliveryCharge, | |
Incentives: item.Pricing.MSRPDiscount, | |
NetPrice: item.Pricing.MSRP | |
}; | |
break; | |
case "AZ": | |
item.DisplayPricing = { | |
BasePrice: item.Pricing.BaseAZPlan, | |
Options: item.Pricing.OptionsAZPlan, | |
Destination: item.Pricing.DestinationDeliveryCharge, | |
Incentives: item.Pricing.AZPlanDiscount, | |
NetPrice: item.Pricing.AZPlan | |
}; | |
break; | |
case "X": | |
item.DisplayPricing = { | |
BasePrice: item.Pricing.BaseXPlan, | |
Options: item.Pricing.OptionsXPlan, | |
Destination: item.Pricing.DestinationDeliveryCharge, | |
Incentives: item.Pricing.XPlanDiscount, | |
NetPrice: item.Pricing.XPlan | |
}; | |
break; | |
} | |
} | |
}; | |
}()), | |
/** | |
* _getPlanType | |
* | |
* Get AXZ Auth status | |
*/ | |
_getPlanType: function () { | |
var axz = NGAW.planType; | |
var type; | |
switch (axz) { | |
case "A/Z Plan": | |
type = "AZ"; | |
break; | |
case "X Plan": | |
type = "X"; | |
break; | |
default: | |
type = "MSRP"; | |
break; | |
} | |
return type; | |
}, | |
/** | |
* _sortItemsIntoArraysByModel | |
* | |
* Pull each node out of the data objects and place them into | |
* arrays oraganized by model and data type. Also, set the | |
* `recent` item | |
*/ | |
_sortItemsIntoArraysByModel: function (index, item) { | |
var name = item.Model; | |
if (name && item.ItemType !== "technology") { | |
D.__models[name] = D.__models[name] || { | |
cpo: [], | |
config: [], | |
inventory: [], | |
compare: [], | |
gallery: [] | |
}; | |
} | |
/* push item into the appropriate bucket */ | |
switch (item.ItemType) { | |
case "technology": | |
item.dataType = "technology"; | |
D.__technology.push(item); | |
break; | |
default: | |
D.__models[name][item.dataType].push(item); | |
D.__totalVehicles++; | |
break; | |
} | |
}, | |
/** | |
* _sortModelsIntoArraysByVehicle | |
* | |
* Place models into the vehicles array | |
*/ | |
_sortModelsIntoArraysByVehicle: function (name, model) { | |
/* get first visible category */ | |
// var firstCategory = model.config.length ? "config" : model.inventory.length ? "inventory" : model.cpo.length ? "cpo" : model.gallery.length ? "gallery" : "config"; | |
var firstCategory = "config"; | |
/* add string form of length for templating */ | |
model.config.count = String(model.config.length); | |
model.inventory.count = String(model.inventory.length); | |
model.compare.count = String(model.compare.length); | |
model.cpo.count = String(model.cpo.length); | |
model.gallery.count = String(model.gallery.length); | |
/* add onto vehicles array */ | |
D.__vehicles.push({ | |
model: name, | |
modelID: name.match(/[a-z0-9]/gi).join("").toLowerCase(), | |
items: model, | |
firstCategory: firstCategory | |
}); | |
}, | |
/** | |
* _sortModelsIntoArrayForUpdates | |
* | |
* Creates array of model names to pass into MySavedItems.Page._getUpdatesHandler | |
*/ | |
_sortModelsIntoArrayForUpdates: function (key, item) { | |
var model = item.Model; | |
var exists = $.inArray(model, D.__vehiclesUpdatesArray); | |
/*only want to add particular ngpID once */ | |
if (exists !== -1 || model === "") { | |
return; | |
} | |
model = model.replace(/%20/g, " "); | |
D.__vehiclesUpdatesArray.push(model); | |
} | |
}; | |
}()); | |
}(window, jQuery, ngbs, NextGen, MySavedItems)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery,ngbs,NextGen,MySavedItems,s, __params */ | |
/** | |
* MySavedItems.Page | |
* | |
* Copyright (c) 2012, Team Detroit | |
* All rights reserved | |
* | |
* @desc Renders and manages the My Saved Items page | |
* @dependencies jQuery + .views, .render, .observable | |
*/ | |
(function (window, $, ngbs, NextGen, MySavedItems) { | |
"use strict"; | |
var document = window.document; | |
var console = window.console || { | |
log: function () {} | |
}; | |
$.extend(MySavedItems.Events, { | |
"PAGE_RENDER_RESET": "PAGE_RENDER_RESET", | |
"PAGE_RENDER_START": "PAGE_RENDER_START" | |
}); | |
var RESPONDERS = { | |
"config": true, | |
"cpo": true, | |
"inventory_bp": true, | |
"inventory_si": true | |
}; | |
var NAMESPACE = ".MSIPAGE"; | |
/* shortcuts */ | |
var $MSI = $(MySavedItems); | |
var E = MySavedItems.Events; | |
var D = MySavedItems.Data; | |
var NG = NextGen; | |
var NGAW = NG.AuthenticationWidget; | |
var NGSAPI = NG.SaveAPI; | |
var message_bus = ngbs._message_bus; | |
var $dealerName; | |
/** | |
* MySavedItems Page module | |
*/ | |
var P = MySavedItems.Page = (function () { | |
/* public interface */ | |
return { | |
/** | |
* init | |
* | |
* Sets up the element for rendering | |
* | |
* @param {jQuery} selector References the element where view is created | |
*/ | |
init: function ($element, options) { | |
/* set wrapper element */ | |
P.$body = $(document.body); | |
P.$element = $element; | |
P.$element.initHTML = $element.html(); | |
P.$flip = $("#msi-flip"); | |
P.$flipContent = $("#msi-flip-conent"); | |
P.$flipBackground = $("#msi-flip-background"); | |
P.$defaultInput = 22 + "px"; | |
/*metrics*/ | |
P.vehicle = ""; | |
P.tabname = ""; | |
P.tabtype = ""; | |
P.category = ""; | |
P.prop5 = ""; | |
/* move flip to child of body */ | |
P.$flip.detach().appendTo(P.$body); | |
/* bind handlers */ | |
P._unbindHandlers(); | |
P._bindHandlers(); | |
/* routing */ | |
P._addRoutes(); | |
/* metrics*/ | |
P._configMetrics(); | |
/* disclosures */ | |
P._getDisclosures(); | |
}, | |
/** | |
* _unbindHandlers | |
*/ | |
_unbindHandlers: function () { | |
$MSI.unbind(NAMESPACE); | |
P.$body.undelegate(NAMESPACE); | |
}, | |
/** | |
* _bindHandlers | |
* | |
* Bind handlers to msi content events | |
*/ | |
_bindHandlers: function () { | |
$MSI.bind(E.PAGE_RENDER_RESET + NAMESPACE, P._renderReset); | |
$MSI.bind(E.PAGE_RENDER_START + NAMESPACE, P._renderAll); | |
P.$body.delegate(".msi-link", "click" + NAMESPACE, P._linkHandler); | |
P.$body.delegate(".msi-sign-out", "click" + NAMESPACE, P._signoutHandler); | |
P.$body.delegate(".msi-sign-in", "click" + NAMESPACE, P._signinHandler); | |
P.$body.delegate(".msi-upgrade-user", "click" + NAMESPACE, P._upgradeHandler); | |
P.$body.delegate(".msi-delete-all", "click" + NAMESPACE, P._deleteAllHandler); | |
P.$body.delegate(".btn-remove", "click" + NAMESPACE, P._deleteHandler); | |
P.$body.delegate(".msi-flip-close", "click" + NAMESPACE, P._hideFlip); | |
P.$body.delegate(".msi-flip-background", "click" + NAMESPACE, P._hideFlip); | |
P.$body.delegate(".axz-exit", "click" + NAMESPACE, P._axzExit); | |
P.$body.delegate(".msi-btn-disclosures", "click" + NAMESPACE, P._disclosuresHandler); | |
P.$body.delegate(".msi-note-field", "keyup" + NAMESPACE, P._textareaChangeHandler); | |
P.$body.delegate(".msi-note-field", "focusin" + NAMESPACE, P._textareaFocusHandler); | |
P.$body.delegate(".msi-note-field", "focusout" + NAMESPACE, P._textareaFocusHandler); | |
P.$body.delegate(".msi-btn-note-submit", "click" + NAMESPACE, P._updateItemHandler); | |
P.$body.delegate(".msi-get-updates", "click" + NAMESPACE, P._getUpdatesHandler); | |
P.$body.delegate('.item-offerstext', 'click' + NAMESPACE, P._showOffersHandler); | |
/* tooltip */ | |
P.$body.delegate(".item-price-info", "mouseenter mouseleave touchstart touchend" + NAMESPACE, P._tooltipHandler); | |
/* hide flip on esc key */ | |
$(document).keyup(function (e) { | |
if (e.keyCode === 27) { | |
P._hideFlip(); | |
} | |
}); | |
/* hide flip on clicking back button */ | |
window.onhashchange = function () { | |
P._hideFlip(); | |
}; | |
}, | |
/** | |
* _addRoutes | |
* | |
* Add deeplinking routes | |
* Fire page metrics | |
* data.tabname = config || gallery || cpo...etc | |
* data.category = recent || technology OR data.vehicle = vehicle | |
*/ | |
_addRoutes: function () { | |
// nav | |
var lcswitch = $("#concierge-switch").data('lcswitch'); | |
var make = __params.make.toLowerCase(); | |
P.deeplink = ngbs.u.deeplink({ | |
"default": function (hash) { | |
var tabname; | |
var vehicle; | |
if (MySavedItems.SavedItems.total === 0) { | |
P.vehicle = ""; | |
P.category = "empty"; | |
P.tabname = "bp"; | |
P.prop5 = "myfolder: empty queue"; | |
P._navigateToContent("msi-vehicle", "empty", "config"); | |
} else { | |
tabname = MySavedItems.SavedItems.recent.dataType; | |
if (tabname === "config") { | |
tabname = "bp"; | |
} | |
vehicle = MySavedItems.SavedItems.recent.Model.toLowerCase() || ""; | |
P.vehicle = vehicle; | |
P.tabtype = "recent"; | |
P.tabname = tabname; | |
P.category = "recent save"; | |
P.prop5 = ""; | |
P._navigateToContent("msi-recent"); | |
} | |
if (NGAW.provider !== "ANONYMOUS" && make === "lincoln" && lcswitch === "on") { | |
P._pageMetrics("msi-concierge-page"); | |
} else { | |
P._pageMetrics("msi-page"); | |
} | |
P._matchCompareColumnsHeight(); | |
}, | |
"/recent": function (hash) { | |
var tabname; | |
var vehicle; | |
// mysaveditems page without #nav displays either empty config or recent item | |
// Pass appropriate value to page-load metrics. | |
if (MySavedItems.SavedItems.total === 0) { | |
P.vehicle = ""; | |
P.category = "empty"; | |
P.tabname = ""; | |
} else { | |
tabname = MySavedItems.SavedItems.recent.dataType.toLowerCase(); | |
vehicle = MySavedItems.SavedItems.recent.Model.toLowerCase() || ""; | |
if (tabname === "config") { | |
tabname = "bp"; | |
} | |
P.vehicle = vehicle; | |
P.category = "recent save"; | |
P.tabtype = "recent"; | |
P.tabname = tabname; | |
} | |
P._navigateToContent("msi-recent"); | |
if (NGAW.provider !== "ANONYMOUS" && make === "lincoln" && lcswitch === "on") { | |
P._pageMetrics("msi-concierge-page"); | |
} else { | |
P._pageMetrics("msi-page"); | |
} | |
P._matchCompareColumnsHeight(); | |
}, | |
"/vehicle": function (hash) { | |
P.vehicle = ""; | |
P.tabname = "empty"; | |
P.tabtype = ""; | |
P.category = "vehicles"; | |
P._navigateToContent("msi-vehicle"); | |
if (NGAW.provider !== "ANONYMOUS" && make === "lincoln" && lcswitch === "on") { | |
P._pageMetrics("msi-concierge-page"); | |
} else { | |
P._pageMetrics("msi-page"); | |
} | |
}, | |
"/vehicle/:vehicle/:tab": function (hash, vehicle, tab) { | |
var tabname = tab; | |
if (tabname === "config") { | |
tabname = "bp"; | |
} | |
P.vehicle = vehicle; | |
P.tabname = tabname; | |
P.tabtype = ""; | |
P.category = "vehicles"; | |
P._navigateToContent("msi-vehicle", vehicle, tab); | |
if (NGAW.provider !== "ANONYMOUS" && make === "lincoln" && lcswitch === "on") { | |
P._pageMetrics("msi-concierge-page"); | |
} else { | |
P._pageMetrics("msi-page"); | |
} | |
if (tab === 'compare') { | |
P._matchCompareColumnsHeight(); | |
} | |
}, | |
"/technology": function (hash) { | |
P.vehicle = ""; | |
P.tabname = "technology"; | |
P.tabtype = ""; | |
P.category = "technology"; | |
P._navigateToContent("msi-technology"); | |
if (NGAW.provider !== "ANONYMOUS" && make === "lincoln" && lcswitch === "on") { | |
P._pageMetrics("msi-concierge-page"); | |
} else { | |
P._pageMetrics("msi-page"); | |
} | |
} | |
}); | |
}, | |
/** | |
* _goHome | |
*/ | |
_goHome: function () { | |
P.deeplink.gotoRoute(); | |
}, | |
/** | |
* _navigateToContent | |
* | |
* Show some content | |
* | |
* @param {String} id | |
* @param {String} vehicle | |
* @param {String} tab | |
*/ | |
_navigateToContent: function (id, vehicle, tab) { | |
var contentID = "#" + id; | |
var $content = $(contentID); | |
var $vehicle; | |
var $tab; | |
if (!$content.length) { | |
P._goHome(); | |
return; | |
} | |
P._showElementHideSiblings($content); | |
if (id === "msi-vehicle") { | |
$vehicle = $(contentID + "-" + vehicle); | |
$tab = $(contentID + "-" + vehicle + "-" + tab); | |
if (!$vehicle.length && !$tab.length) { | |
P._goHome(); | |
return; | |
} | |
P._showElementHideSiblings($vehicle); | |
P._showElementHideSiblings($tab); | |
P._textareaFormatHandler($tab); | |
} else { | |
P._textareaFormatHandler(contentID); | |
} | |
P._updateNav(id, vehicle, tab); | |
}, | |
/** | |
* _updateNav | |
* | |
* Update the current class in the nav | |
* | |
* @param {String} id | |
* @param {String} vehicle | |
* @param {String} tab | |
*/ | |
_updateNav: function (id, vehicle, tab) { | |
// highlight main nav | |
P._showElementHideSiblings($("a.nav-" + id).parent()); | |
// if vehicle, highlight vehicle nav | |
P._showElementHideSiblings($("a.nav-" + id + "-" + vehicle).parent()); | |
// if tab, highlight tab nav | |
P._showElementHideSiblings($("a.nav-" + id + "-" + vehicle + "-" + tab).parent()); | |
}, | |
/** | |
* _showElementHideSiblings | |
* | |
* Show an element and hide its siblings | |
* | |
* @param {jQuery} $element | |
*/ | |
_showElementHideSiblings: function ($element) { | |
$element.addClass("current").siblings().removeClass("current"); | |
}, | |
/** | |
* _gotoURL | |
* | |
* Navigate to an external URL after checking if content exists | |
* | |
* {String} itemType | |
* {String} id | |
* {String} url | |
* {String} model optional | |
* {Boolean} newWin | |
*/ | |
_gotoURL: function (itemType, id, url, model, newWin) { | |
P._processingFlip(); | |
var success = function (data, isSuccessful) { | |
var response = data && data.response; | |
var redirect; | |
if (data === true || response.success) { | |
P._hideFlip(); | |
if (newWin) { | |
window.open(url); | |
return false; | |
} | |
window.location.href = url; | |
} else { | |
redirect = response.redirect; | |
P._responderFlip(itemType, id, redirect, model); | |
} | |
}; | |
var error = function () { | |
P._hideFlip(); | |
if (newWin) { | |
window.open(url); | |
return false; | |
} | |
window.location.href = url; | |
}; | |
var responder = RESPONDERS[itemType]; | |
var anchor; | |
var responderDomain; | |
var responderPath; | |
var responderURL; | |
var responderProtocol; | |
// make an ajax call to the responder for the url | |
if (responder) { | |
anchor = ngbs.u.anchor(url); | |
responderProtocol = anchor.protocol(); | |
responderDomain = anchor.host(); | |
responderPath = "/saveandshare/checkurl?url=" + encodeURIComponent(url); | |
responderURL = responderProtocol + "//" + responderDomain + responderPath; | |
$.ajax(responderURL, { | |
dataType: "jsonp", | |
cache: true, | |
timeout: 8000 | |
}).done(success).fail(error); | |
} else { | |
success(true); | |
} | |
}, | |
/** | |
* _renderReset | |
* | |
* Initialize the my saved items canvas | |
*/ | |
_renderReset: function () { | |
P.$element.html(P.$element.initHTML); | |
}, | |
/** | |
* _renderAll | |
* | |
* Render the data as html using the template indicated | |
* | |
* @param {Object} e jQuery event | |
* @param {Object} data Processed saved items data | |
*/ | |
_renderAll: function (e, data) { | |
MySavedItems.SavedItems = data || MySavedItems.SavedItems; | |
P.$element.link(MySavedItems.SavedItems, "#msi-template"); | |
/* start deeplinking */ | |
P.deeplink.start(); | |
/* init sharev2 buttons */ | |
jQuery('.ngbssharev2pagelevel').each(function (index, element) { | |
ngbs.widgetv2.bootstrap.register(element); | |
}); | |
P.$body.ready(function () { | |
$dealerName = $('.bp-saved-dealer-names'); | |
var dealercode; | |
$dealerName.each(function () { | |
dealercode = $dealerName.attr('data-pacode'); | |
jQuery.getJSON('/services/dealer/Details.json?make=Lincoln&dealerPACode=' + dealercode, function (response) { | |
$dealerName.html(response.Response.Dealer.Name); | |
}).fail(function (response) { | |
$dealerName.html('the Dealer'); | |
}); | |
}); | |
}); | |
}, | |
_matchCompareColumnsHeight: function () { | |
var compareItems = jQuery('.msi-content-holder .msi-compare, #msi-recent .msi-compare'); | |
// reset height | |
compareItems.find('.item-sec').css('height', 'auto'); | |
compareItems.each(function () { | |
var tallest = 0, | |
cols = jQuery(this).find('.item-sec'), | |
btnRow = jQuery(this).find('.item-btnrow'); | |
cols.each(function () { | |
var thisHeight = jQuery(this).height(); | |
if (thisHeight > tallest) { | |
tallest = thisHeight; | |
} | |
}); | |
cols.height(tallest); | |
btnRow.css('top', tallest + 77); | |
}); | |
}, | |
/** | |
* _tooltipHandler | |
* | |
* Hover for tooltip | |
* | |
* @param {Object} e jQuery event | |
*/ | |
_tooltipHandler: function (e) { | |
var $this = $(this); | |
var $tooltip = $this.find(".msi-tooltip"); | |
if (e.type === "mouseenter" || e.type === "touchstart") { | |
$tooltip.show(); | |
} else { | |
$tooltip.hide(); | |
} | |
}, | |
/** | |
* _textareaResizeHandler | |
* | |
* Finds and resizes all textarea fields to fit populated text, styles populated text fields appropriately. | |
* | |
*/ | |
_textareaFormatHandler: function (element) { | |
var inputFields = $(element).find("textarea.msi-note-field"); | |
if (inputFields.length > 0) { | |
$.each(inputFields, function (index, input) { | |
var populated = $(input).val(); | |
if (populated) { | |
var submitBtn = $(this).parent().find(".msi-btn-note-submit"); | |
var padding = parseFloat($(input).css("padding-top")) + parseFloat($(input).css("padding-bottom")); | |
var height = input.scrollHeight - padding; | |
$(submitBtn).addClass("populated").text("Edit"); | |
$(input).addClass("populated"); | |
$(input).height(height); | |
if (submitBtn.hasClass("updated")) { | |
$(submitBtn).text("Submit"); | |
} | |
} | |
}); | |
} | |
}, | |
/** | |
* _textareaFocusHandler | |
* | |
* Handle series of display changes when user focuses on notes field | |
* | |
* @param {Event} e | |
*/ | |
_textareaFocusHandler: function (e) { | |
if (e.type === "focusin") { | |
$(this).addClass("selected"); | |
} else { | |
$(this).removeClass("selected"); | |
} | |
}, | |
/** | |
* _textareaChangeHandler | |
* | |
* Handle series of behaviors to occur when user changes value of input. | |
* | |
* @param {Event} e | |
*/ | |
_textareaChangeHandler: function (e) { | |
var $this = $(this); | |
var $submitBtn = $this.parent().find(".msi-btn-note-submit"); | |
$submitBtn.addClass("updated").text("Submit"); | |
$this.addClass("updated"); | |
while ($this.outerHeight() < this.scrollHeight + parseFloat($this.css("borderTopWidth")) + parseFloat($this.css("borderBottomWidth"))) { | |
$this.height($this.height() + 1); | |
} | |
}, | |
/** | |
* _linkHandler | |
* | |
* Handle outgoing links | |
* | |
* @param {Event} e | |
*/ | |
_linkHandler: function (e) { | |
e.preventDefault(); | |
var $this = $(this); | |
var $item = $this.parents(".msi-item"); | |
var url = $this.attr("href"); | |
var data = $item.readDataAttr(); | |
var itemType = data.itemType; | |
var id = data.id; | |
var model = data.model; | |
var newWin = $this.hasClass('msi-newwin'); | |
P._gotoURL(itemType, id, url, model, newWin); | |
}, | |
/** | |
* _showOffersHandler | |
* | |
* Handle view offers click event | |
* | |
* @param {Event} e | |
*/ | |
_showOffersHandler: function (e) { | |
var $flip = jQuery('.msi-incentives-flip-content'), | |
$flipHolder = $flip.find('.msi-incentives-holder'), | |
$target = jQuery(e.target), | |
vehicleData = $target.closest('.msi-item').data(), | |
zipcode = vehicleData.zipcode, | |
year = vehicleData.year, | |
make = vehicleData.make, | |
ngpID = vehicleData.model, | |
url = 'https://www.ford-incentives.com/Flip/', | |
iframe = document.createElement('iframe'); | |
if (make === 'lincoln') { | |
url = "https://www.lincoln-incentives.com/Flip/"; | |
} | |
var params = [ | |
'zipcode=' + zipcode, | |
'year=' + year, | |
'make=' + make, | |
'model=' + ngpID, | |
'app_context=ngbs_sploffr' | |
]; | |
url += '?' + params.join('&'); | |
$flipHolder.html(''); | |
iframe.src = url; | |
iframe.id = 'incentives-flip-iframe'; | |
iframe.height = "580"; | |
iframe.width = '950'; | |
iframe.frameBorder = 0; | |
$flipHolder.append(iframe); | |
P._showFlip("msi-incentives-flip"); | |
}, | |
/** | |
* _signinHandler | |
* | |
* @param {Event} e | |
*/ | |
_signinHandler: function (e) { | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
NGAW.validateUser(NG.widgets.MY_SAVED_ITEM_WIDGET_NAME); | |
}, | |
/** | |
* _upgradeHandler | |
* | |
* @param {Event} e | |
*/ | |
_upgradeHandler: function (e) { | |
NGAW.upgradeUser(); | |
}, | |
/** | |
* _signoutHandler | |
* | |
* @param {Event} e | |
*/ | |
_signoutHandler: function (e) { | |
NGAW.logoutUser(); | |
var eVar11 = s.eVar11; | |
var channel = s.channel; | |
var hier1 = s.hier1; | |
var metricsData = { | |
eVar11: eVar11, | |
channel: channel, | |
hier1: hier1 | |
}; | |
ngbs.metrics.constructor(); | |
P._clickMetrics("logout", metricsData); | |
P._confirmFlip("msi-logout-complete"); | |
}, | |
/** | |
* _deleteAllHandler | |
* | |
* @param {Event} e | |
*/ | |
_deleteAllHandler: function (e) { | |
var callbacks = { | |
"ok": function () { | |
NGSAPI.deleteSavedItems(function () { | |
$MSI.trigger(E.DATA_REQUEST_START); | |
P._confirmFlip("msi-delete-all-complete"); | |
}); | |
} | |
}; | |
P._showFlip("msi-delete-all", callbacks); | |
}, | |
/** | |
* _deleteHandler | |
* | |
* @param {Event} e | |
*/ | |
_deleteHandler: function (e) { | |
var $this = $(this); | |
var $item = $this.parents(".msi-item"); | |
var data = $item.readDataAttr(); | |
var id = data.id; | |
var itemType = data.itemType; | |
var callbacks = { | |
"ok": function () { | |
NGSAPI.deleteItem(itemType, id, function () { | |
$MSI.trigger(E.DATA_REQUEST_START); | |
P._confirmFlip("msi-delete-complete"); | |
}); | |
} | |
}; | |
P._showFlip("msi-delete", callbacks); | |
}, | |
/** | |
* _updateItemHandler | |
* | |
* @param {Event} e | |
*/ | |
_updateItemHandler: function (e) { | |
var $element = $(this); | |
var isUpdated = $element.hasClass("updated"); | |
if (!isUpdated) { | |
return; | |
} | |
NextGen.DataCollectionWidget.formObjectAndUpdateItem($element); | |
P._clickMetrics("notes"); | |
}, | |
/** | |
* _responderDeleteHandler | |
* | |
* @param {Event} id | |
*/ | |
_responderDeleteHandler: function (id, itemType, redirectCallback) { | |
NGSAPI.deleteItem(itemType, id, function () { | |
if (redirectCallback) { | |
redirectCallback(); | |
} else { | |
$MSI.trigger(E.DATA_REQUEST_START); | |
P._confirmFlip("msi-delete-complete"); | |
} | |
}); | |
}, | |
/** | |
* _disclosuresHandler | |
* | |
* @param {Event} e | |
*/ | |
_disclosuresHandler: function (e) { | |
P._showFlip("msi-disclosures"); | |
}, | |
/** | |
* _getUpdatesHandler | |
* | |
* @param {Event} e | |
*/ | |
_getUpdatesHandler: function (e) { | |
var $getupdates = $('.ng-widget.getupdates'); | |
var make = __params.make.toLowerCase(); | |
var qaCodes = "313167"; | |
if (make === "lincoln") { | |
qaCodes = "321017"; | |
} | |
ngbs._message_bus.send($getupdates, 'getupdates.open', { | |
type: 'updates', | |
vehicles: MySavedItems.Data.__vehiclesUpdatesArray, | |
qacodes: qaCodes | |
}); | |
}, | |
/** | |
* _getDisclosures | |
*/ | |
_getDisclosures: function () { | |
var urls = { | |
ford: "/services/attributes/Attributes.json?make=Ford&model=Focus&year=2012&returnAttributes=ShoppingTools_Shared_Disclaimer_Text_.*", | |
lincoln: "/services/attributes/Attributes.json?make=Lincoln&model=MKX&year=2012&returnAttributes=ShoppingTools_Shared_Disclaimer_Text_.*" | |
}; | |
var make = __params.make.toLowerCase(); | |
var url = urls[make]; | |
if (url) { | |
$.get(url).success(P._renderDisclosures); | |
} | |
}, | |
/** | |
* _renderDisclosures | |
*/ | |
_renderDisclosures: function (data) { | |
var $disclosuresHolder = $(".msi-disclosures-holder"); | |
var sections; | |
var text = ""; | |
if (data.Response && data.Response.Attribute) { | |
sections = data.Response.Attribute; | |
$.each(sections, function (index, section) { | |
text += section.Value; | |
}); | |
$disclosuresHolder.append(text); | |
} | |
}, | |
/** | |
* _showFlip | |
* | |
* Fires _toggleFlipContents. Appends css to display flip | |
* | |
* @param {String} id Required string content identifier | |
* @param {Object} callbacks Map of callback functions to classes | |
*/ | |
_showFlip: function (id, callbacks) { | |
callbacks = callbacks || {}; | |
/* check for cancel */ | |
callbacks.cancel = callbacks.cancel || function () { | |
P._hideFlip(); | |
}; | |
/* bind callbacks to buttons in flip */ | |
P.$flip.undelegate(); | |
$.each(callbacks, function (i, callback) { | |
P.$flip.delegate(".msi-" + i, "click", callback); | |
}); | |
/* change flip contents and show */ | |
P._toggleFlipContents(id); | |
P.$flip.show(); | |
}, | |
/** | |
* _hideFlip | |
* | |
* Hides flip | |
*/ | |
_hideFlip: function () { | |
P.$flip.hide(); | |
}, | |
/** | |
* _confirmFlip | |
* | |
* @param {String} id Required id of content flip minus "-content" | |
*/ | |
_confirmFlip: function (id) { | |
var callbacks = { | |
"confirm": function () { | |
P._hideFlip(); | |
} | |
}; | |
P._showFlip(id, callbacks); | |
}, | |
/** | |
* _toggleFlipContents | |
* | |
* Hides and shows flip content | |
* | |
* @param {Object} id Item clicked to trigger event | |
*/ | |
_toggleFlipContents: function (id) { | |
var $flipContent = $("." + id + "-content"); | |
$flipContent.show(); | |
$flipContent.siblings().hide(); | |
}, | |
/** | |
* _axzAuthFlip | |
* | |
* @param {Event} e | |
* @param {String} planType | |
*/ | |
_axzAuthFlip: (function () { | |
var shownOnce = false; | |
/*retrieving location from url for metrics*/ | |
var url = window.location.href; | |
var section = url.split("/"); | |
section = (section[section.length - 1]); | |
if (section === "config") { | |
section = "bp"; | |
} | |
switch (section) { | |
case "bp": | |
case "cpo": | |
case "inventory": | |
case "gallery": | |
case "technology": | |
break; | |
default: | |
section = "recent"; | |
break; | |
} | |
var metricsData = { | |
tabname: section | |
}; | |
return function (e, planType) { | |
if (!shownOnce) { | |
if (planType === "AZ") { | |
P._showFlip("msi-az-authentication"); | |
P._pageMetrics("axz-flip", metricsData); | |
} else if (planType === "X") { | |
P._showFlip("msi-x-authentication"); | |
P._pageMetrics("axz-flip", metricsData); | |
} | |
shownOnce = true; | |
} | |
}; | |
}()), | |
/** | |
* _axzExit | |
* Fires metrics for AXZ exit link | |
*/ | |
_axzExit: function () { | |
var eVar11 = s.eVar11; | |
var metricsData = { | |
eVar11 : eVar11 | |
}; | |
P._clickMetrics("axz-exit", metricsData); | |
}, | |
/** | |
* _responderFlip | |
* | |
* @param {String} itemType | |
* @param {String} id | |
* @param {String} url | |
* @param {String} model optional | |
*/ | |
_responderFlip: function (itemType, id, url, model) { | |
var callbacks = { | |
"similar": function () { | |
var input = $(this).parent().parent().find("input"); | |
P._hideFlip(); | |
if (input.attr("checked")) { | |
P._responderDeleteHandler(id, itemType, function () { | |
window.location.href = url; | |
}); | |
} | |
window.location.href = url; | |
}, | |
"cancel": function () { | |
var input = $(this).parent().parent().find("input"); | |
P._hideFlip(); | |
if (input.attr("checked")) { | |
P._responderDeleteHandler(id, itemType); | |
} | |
} | |
}; | |
switch (itemType) { | |
case "cpo": | |
P._showFlip("msi-responder-cpo", callbacks); | |
break; | |
case "inventory_bp": | |
case "inventory_si": | |
P._showFlip("msi-responder-inventory", callbacks); | |
break; | |
case "config": | |
P._showFlip("msi-responder-config", callbacks); | |
break; | |
default: | |
break; | |
} | |
var metricsItemType; | |
/*metrics requires more generalized values for itemType | |
* while services require full values. Ex: inventory_si VS inventory. | |
*/ | |
if (itemType === "inventory_si" || itemType === "inventory_bp") { | |
metricsItemType = "inventory"; | |
} else if (itemType === "config") { | |
metricsItemType = "bp"; | |
} else { | |
metricsItemType = itemType; | |
} | |
var metricsData = { | |
tabName: metricsItemType, | |
vehicle: model | |
}; | |
P._pageMetrics("obsolete-item", metricsData); | |
}, | |
/** | |
* _processingFlip | |
* | |
* Show a loading image while processing is being done | |
*/ | |
_processingFlip: function () { | |
P._showFlip("msi-processing"); | |
}, | |
/** | |
* _configMetrics | |
* | |
* Initial setup of metrics | |
*/ | |
_configMetrics: function () { | |
var base; | |
var make = __params.make.toLowerCase(); | |
if (make === "ford") { | |
base = "fv: myfolder: "; | |
} else { | |
base = "ln: myfolder: "; | |
} | |
var getPageName = function () { | |
var details; | |
if (P.vehicle !== "empty" && P.vehicle !== "") { | |
details = ": " + make + " " + P.vehicle; | |
} else { | |
details = ": " + P.category; | |
} | |
return base + "saved items: " + P.tabname + details; | |
}; | |
var getProp16 = function () { | |
var prop; | |
if (P.vehicle !== "empty" && P.vehicle !== "") { | |
prop = make + " " + P.vehicle; | |
} else { | |
prop = ""; | |
} | |
return prop.toLowerCase(); | |
}; | |
ngbs.metrics.config({ | |
click: { | |
'logout': { | |
pe: "o", | |
pev2: base + "signout", | |
channel: function (data) { | |
return data.channel; | |
}, | |
hier1: function (data) { | |
return data.hier1; | |
}, | |
prop5: "", | |
prop11: function (data) { | |
return data.eVar11; | |
}, | |
eVar11: function (data) { | |
return data.eVar11; | |
}, | |
prop16: getProp16, | |
eVar16: getProp16, | |
prop42: "logged out" | |
}, | |
'notes': { | |
pe: "o", | |
pev2: base + "notes", | |
channel: function (data) { | |
return data.channel; | |
}, | |
hier1: function (data) { | |
return data.hier1; | |
}, | |
prop5: "", | |
prop11: function (data) { | |
return data.eVar11; | |
}, | |
eVar11: function (data) { | |
return data.eVar11; | |
}, | |
prop16: getProp16, | |
eVar16: getProp16 | |
}, | |
'axz-exit': { | |
pe: "e", | |
pev2: "referral: exit", | |
channel: "myfolder", | |
prop5: "referral: axzplan: login", | |
hier1: "shopping tools:myfolder", | |
prop6: "axzplan: login", | |
eVar6: "axzplan: login", | |
prop11: function (data) { | |
return data.eVar11; | |
}, | |
eVar11: function (data) { | |
return data.eVar11; | |
}, | |
prop16: getProp16, | |
eVar16: getProp16 | |
} | |
}, | |
page: { | |
'msi-page': { | |
pageName: getPageName, | |
channel: "myfolder", | |
prop5: function () { | |
return P.prop5; | |
}, | |
hier1: function () { | |
return "shopping tools:myfolder:" + P.tabname; | |
}, | |
prop11: function () { | |
if (P.tabtype === "recent") { | |
return base + "saved items: " + P.tabtype; | |
} | |
return base + "saved items: " + P.tabname; | |
}, | |
eVar11: function () { | |
if (P.tabtype === "recent") { | |
return base + "saved items: " + P.tabtype; | |
} | |
return base + "saved items: " + P.tabname; | |
}, | |
prop16: getProp16, | |
eVar16: getProp16, | |
prop39: "", | |
events: [""] | |
}, | |
'msi-concierge-page': { | |
pageName: function () { | |
if (P.tabtype === "recent") { | |
return "ln:concierge:saved items:" + P.tabtype; | |
} | |
return "ln:concierge:saved items:" + P.tabname; | |
}, | |
channel: "concierge", | |
hier1: "shopping tools:concierge:my saved items", | |
prop11: function () { | |
if (P.tabtype === "recent") { | |
return "ln:concierge:my saved items:" + P.tabtype; | |
} | |
return "ln:concierge:my saved items:" + P.tabname; | |
}, | |
eVar11: function () { | |
if (P.tabtype === "recent") { | |
return "ln:concierge:my saved items:" + P.tabtype; | |
} | |
return "ln:concierge:my saved items:" + P.tabname; | |
}, | |
prop39: "", | |
events: [""] | |
}, | |
'axz-flip': { | |
prop39: "", | |
pageName: base + "axz flip", | |
channel: "myfolder", | |
hier1: "shopping tools:myfolder", | |
prop5: base + "axz flip", | |
prop11: function (data) { | |
return base + "saved items: " + data.tabname; | |
}, | |
eVar11: function (data) { | |
return base + "saved items: " + data.tabname; | |
}, | |
eVar16: getProp16, | |
prop16: getProp16, | |
events: [""] | |
}, | |
'obsolete-item': { | |
pageName: function () { | |
return base + "item not available: " + P.tabname; | |
}, | |
channel: "myfolder", | |
prop5: function () { | |
return "myfolder:item not available: " + P.tabname; | |
}, | |
hier1: "shopping tools:myfolder", | |
prop11: function () { | |
return base + "item not available:" + P.tabname; | |
}, | |
eVar11: function () { | |
return base + "item not available: " + P.tabname; | |
}, | |
prop16: getProp16, | |
eVar16: getProp16, | |
prop39: "", | |
events: [""] | |
} | |
} | |
}); | |
}, | |
/** | |
* _clickMetrics | |
* | |
* Fires click metrics | |
* | |
* @param {String} clickName | |
* @param {Object} data | |
*/ | |
_clickMetrics: function (clickName, data) { | |
ngbs.metrics.click(clickName, data); | |
}, | |
/** | |
* _pageMetrics | |
* | |
* Fires page level metrics | |
* | |
* @param {String} pageName | |
* @param {Object} data | |
*/ | |
_pageMetrics: function (pageName, data) { | |
ngbs.metrics.page(pageName, data); | |
} | |
}; | |
}()); | |
}(window, jQuery, ngbs, NextGen, MySavedItems)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery,ThirdPartyAuthenticator,AXZAuthenticator,s, ngbs, MySavedItems, Concierge, shopOpenScheduleDealer, _lc_params */ | |
/** | |
* NextGen.SaveAPI | |
* | |
* Copyright (c) 2012, Team Detroit | |
* All rights reserved | |
* | |
* @desc Manages interaction with the Save API | |
* | |
* | |
* @fileoverview <h1>SAVE - Authentication Widget Library</h1> | |
* <p>This is a library meant to Authenticate the User with 4 different | |
* Social Media Sites. "Facebook", "Google", "LinkedIn","Twitter" | |
* widget. The Save Widget will access the ng_authentication_window | |
* internally. While MSI will access it using the validateUser() function</p> | |
* | |
* <strong>Version History:</strong> | |
* <ul> | |
* <li>1.0: Initial creation of script</li> | |
* <li>1.1: Removal of global . Optimizations of Code. Adding into the NGBS Library</li> | |
* </ul> | |
* | |
* @version 1.1 | |
* | |
* | |
* @fileoverview <h1>SAVE - SAVE Widget Library</h1> | |
* <p>This is a library meant to SAVE the required data | |
* passed from VIEW / Web Pages to Save Widget. Based on | |
* Authentication Status, save flow will perform desire | |
* funcationality | |
* | |
* <strong>Version History:</strong> | |
* <ul> | |
* <li>1.0: Initial creation of script</li> | |
* <li>1.1: Removal of global . Optimizations of Code. Adding into the NGBS Library</li> | |
* </ul> | |
* | |
* @version 1.1 | |
*/ | |
(function (window, $, ngbs) { | |
"use strict"; | |
var document = window.document; | |
var location = window.location; | |
var console = window.console || { | |
log: function () {} | |
}; | |
var NextGen = window.NextGen = window.NextGen || {}; | |
var $NextGen = $(NextGen); | |
NextGen.baseURL = null; | |
NextGen.ready = false; | |
NextGen.events = { | |
"AUTHENTICATION_COMPLETE_EVENT": "AUTHENTICATION_COMPLETE_EVENT", | |
"METRIC_TRACKER_EVENT": "METRIC_TRACKER_EVENT", | |
"AUTHENTICATION_STARTED": "AUTHENTICATION_STARTED", | |
"AUTHENTICATION_ENDED": "AUTHENTICATION_ENDED", | |
"AUTHENTICATION_LOGOUT_EVENT": "AUTHENTICATION_LOGOUT_EVENT", | |
"SAVE_STARTED": "SAVE_STARTED", | |
"SAVE_ENDED": "SAVE_ENDED" | |
}; | |
NextGen.widgets = { | |
"MY_SAVED_ITEM_WIDGET_NAME": "MSI", | |
"UPGRADE_USER": "UPGRADE_USER", | |
"SAVE_WIDGET_NAME": "SAVE" | |
}; | |
/** | |
* NextGen Save API module | |
*/ | |
NextGen.SaveAPI = (function () { | |
/* begin module */ | |
var NGSAPI = function () {}; | |
/* add methods */ | |
NGSAPI.prototype = { | |
/** | |
* init | |
* Set user-specific values | |
*/ | |
init: function () {}, | |
/** | |
* getBase | |
*/ | |
getBase: function () { | |
return "/services/user/" + NextGen.AuthenticationWidget.userId + "/SavedItems"; | |
}, | |
/** | |
* getAccessToken | |
*/ | |
getAccessToken: function () { | |
return NextGen.AuthenticationWidget.accessToken; | |
}, | |
/** | |
* getBrandSite | |
*/ | |
getBrandSite: function () { | |
return NextGen.make === "Ford" ? "www.ford.com" : "www.lincoln.com"; | |
}, | |
/** | |
* getParameters | |
*/ | |
getParameters: function () { | |
return "accessToken=" + this.getAccessToken() + "&brandSite=" + this.getBrandSite(); | |
}, | |
/** | |
* makeRequest | |
* | |
* Makes all AJAX requests for this module | |
* @param {String} url The request URL | |
* @param {Object} props AJAX request properties | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
*/ | |
makeRequest: function (url, props, success, error) { | |
if (typeof error === "undefined") { | |
error = this.defaultErrorHandler; | |
} | |
/* return a reference to the ajax promise */ | |
return $.ajax(url, props).done(success).fail(error); | |
}, | |
/** | |
* defaultErrorHandler | |
* | |
* Default error callback for AJAX requests | |
*/ | |
defaultErrorHandler: function (jqxhr, type, status) { | |
throw new Error(status); | |
}, | |
/** | |
* getSavedItems | |
* | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
getSavedItems: function (success, error) { | |
var cacheBuster = +new Date(); | |
var url = this.getBase() + ".json" + "?" + this.getParameters() + "&t=" + cacheBuster; | |
var props = { | |
type: "GET" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteSavedItems | |
* | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteSavedItems: function (success, error) { | |
var url = this.getBase() + "?" + this.getParameters(); | |
var props = { | |
type: "DELETE" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* createConfigVehicle | |
* | |
* @param {Object} config Parameters for the item to create | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @param {String} optional, itemID to update item (if id is present = item already exists) | |
* @return {Object} jqXHR deferred object | |
*/ | |
createConfigVehicle: function (config, success, error, itemID) { | |
var url; | |
if (itemID) { | |
url = this.getBase() + "/ConfigVehicle/" + itemID + "?" + this.getParameters(); | |
} else { | |
url = this.getBase() + "/ConfigVehicle/" + "?" + this.getParameters(); | |
} | |
var props = { | |
type: "POST", | |
data: config | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteConfigVehicle | |
* | |
* @param {String} id The id of the item to delete | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteConfigVehicle: function (id, success, error) { | |
var url = this.getBase() + "/ConfigVehicle/" + id + "?" + this.getParameters(); | |
var props = { | |
type: "DELETE" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* createInventoryVehicle | |
* | |
* @param {Object} config Parameters for the item to create | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @param {String} optional, itemID to update item | |
* @return {Object} jqXHR deferred object | |
*/ | |
createInventoryVehicle: function (config, success, error, itemID) { | |
var url; | |
if (itemID) { | |
url = this.getBase() + "/InventoryVehicle/" + itemID + "?" + this.getParameters(); | |
} else { | |
url = this.getBase() + "/InventoryVehicle/" + "?" + this.getParameters(); | |
} | |
var props = { | |
type: "POST", | |
data: config | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteInventoryVehicle | |
* | |
* @param {String} id The id of the item to delete | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteInventoryVehicle: function (id, success, error) { | |
var url = this.getBase() + "/InventoryVehicle/" + id + "?" + this.getParameters(); | |
var props = { | |
type: "DELETE" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* createCPOVehicle | |
* | |
* @param {Object} config Parameters for the item to create | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @param {String} optional, itemID to update item | |
* @return {Object} jqXHR deferred object | |
*/ | |
createCPOVehicle: function (config, success, error, itemID) { | |
var url; | |
if (itemID) { | |
url = this.getBase() + "/CPOVehicle/" + itemID + "?" + this.getParameters(); | |
} else { | |
url = this.getBase() + "/CPOVehicle/" + "?" + this.getParameters(); | |
} | |
var props = { | |
type: "POST", | |
data: config | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteCPOVehicle | |
* | |
* @param {String} id The id of the item to delete | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteCPOVehicle: function (id, success, error) { | |
var url = this.getBase() + "/CPOVehicle/" + id + "?" + this.getParameters(); | |
var props = { | |
type: "DELETE" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* createBrandContent | |
* | |
* @param {Object} config Parameters for the item to create | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @param {String} optional, itemID to update item | |
* @return {Object} jqXHR deferred object | |
*/ | |
createBrandContent: function (config, success, error, itemID) { | |
var url; | |
if (itemID) { | |
url = this.getBase() + "/BrandContent/" + itemID + "?" + this.getParameters(); | |
} else { | |
url = this.getBase() + "/BrandContent/" + "?" + this.getParameters(); | |
} | |
var props = { | |
type: "POST", | |
data: config | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteBrandContent | |
* | |
* @param {String} id The id of the item to delete | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteBrandContent: function (id, success, error) { | |
var url = this.getBase() + "/BrandContent/" + id + "?" + this.getParameters(); | |
var props = { | |
type: "DELETE" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* createCompareVehicle | |
* | |
* @param {Object} config Parameters for the item to create | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @param {String} optional, itemID to update item | |
* @return {Object} jqXHR deferred object | |
*/ | |
createCompareVehicle: function (config, success, error, itemID) { | |
var url; | |
if (itemID) { | |
url = this.getBase() + "/VehicleCompare/" + itemID + "?" + this.getParameters(); | |
} else { | |
url = this.getBase() + "/VehicleCompare/" + "?" + this.getParameters(); | |
} | |
var props = { | |
type: "POST", | |
data: config | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteCompareVehicle | |
* | |
* @param {String} id The id of the item to delete | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteCompareVehicle: function (id, success, error) { | |
var url = this.getBase() + "/InventoryVehicle/" + id + "?" + this.getParameters(); | |
var props = { | |
type: "DELETE" | |
}; | |
return this.makeRequest(url, props, success, error); | |
}, | |
/** | |
* deleteItem | |
* | |
* @param {String} itemType | |
* @param {String} id The id of the item to delete | |
* @param {Function} success Callback | |
* @param {Function} error Callback | |
* @return {Object} jqXHR deferred object | |
*/ | |
deleteItem: function (itemType, id, success, error) { | |
var methodName; | |
switch (itemType) { | |
case "image": | |
case "video": | |
case "technology": | |
methodName = "deleteBrandContent"; | |
break; | |
case "cpo": | |
methodName = "deleteCPOVehicle"; | |
break; | |
case "inventory_bp": | |
case "inventory_si": | |
methodName = "deleteInventoryVehicle"; | |
break; | |
case "config": | |
methodName = "deleteConfigVehicle"; | |
break; | |
case "compare": | |
methodName = "deleteCompareVehicle"; | |
break; | |
} | |
return NGSAPI[methodName].call(NGSAPI, id, success, error); | |
} | |
}; | |
/* return public properties */ | |
NGSAPI = new NGSAPI(); | |
return NGSAPI; | |
}()); | |
/** | |
* Base Class - Authentication Widget | |
* Core funcationality of User Authentication | |
* using the NextGen Services with Social Media Sites | |
*/ | |
NextGen.AuthenticationWidget = (function () { | |
var NGPOP; | |
var NGSW; | |
var TPA; | |
var AXZ; | |
var NGFBW; | |
var PROVIDERS = { | |
"FACEBOOK": "/services/authentication/oauth/requesttoken/facebook?redirectUrl=", | |
"TWITTER": "/services/authentication/oauth/requesttoken/twitter?redirectUrl=", | |
"GOOGLE": "/services/authentication/openid/discovery/google?redirectUrl=", | |
"YAHOO": "/services/authentication/openid/discovery/yahoo?redirectUrl=", | |
"ANONYMOUS": "/services/authentication/aauth/requesttoken/anonymous?redirectUrl=" | |
}; | |
/* begin module */ | |
var NGAW = function () { | |
this.userId = null; | |
this.accessToken = null; | |
this.provider = null; | |
this.planType = null; | |
this.authenticationWindow = null; | |
this.authenticationTimer = null; | |
this.contentAfterIcon = null; | |
this.divider = null; | |
this.mainContentDiv = null; | |
this.isUpgraded = false; | |
}; | |
/* add methods */ | |
NGAW.prototype = { | |
/** | |
* init | |
*/ | |
init: function () { | |
NGPOP = NextGen.SavePopupWidget; | |
NGSW = NextGen.SaveWidget; | |
NGFBW = NextGen.FallbackWidget; | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
TPA = new window.ThirdPartyAuthenticator(); | |
AXZ = new window.AXZAuthenticator(); | |
NGAW.contentAfterIcon = $("#FNASW_content_after_icon"); | |
NGAW.divider = $("#FNASW_divider"); | |
NGAW.mainContentDiv = $("#main_content_div"); | |
NGAW.bindHandlers(); | |
NGAW.setProperties(); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
$(document).delegate("a[rel='ng_auth_btn_']", "click", function () { | |
var provider = $(this).data("provider"); | |
NGAW.authenticateWithNextGenService(provider); | |
}); | |
}, | |
/** | |
* getFallbackUrl | |
*/ | |
getFallbackUrl: function () { | |
return NextGen.baseURL + "/save/fallback"; | |
}, | |
/** | |
* getProviderUrl | |
*/ | |
getProviderUrl: function (name) { | |
var url = NextGen.baseURL + PROVIDERS[name]; | |
if (url.indexOf("redirectUrl") > -1) { | |
url = url + NGAW.getFallbackUrl(); | |
} | |
return url; | |
}, | |
/** | |
* startPollingForUserID | |
* | |
* Start a timer to check for user id, if cookies are set | |
* call getAuthenticationStatus(true) and cancel timer | |
*/ | |
startPollingForUserID: function () { | |
NGAW.authenticationTimer = window.setTimeout(function () { | |
var userId = TPA.getUserId(); | |
var provider = TPA.getProvider() || "ANONYMOUS"; | |
var model = NGSW.saveData ? NGSW.saveData.model : ""; | |
if (NGAW.callerWidget === NextGen.widgets.UPGRADE_USER) { | |
if (userId && provider !== "ANONYMOUS") { | |
$NextGen.trigger(NextGen.events.METRIC_TRACKER_EVENT, [NextGen.events.AUTHENTICATION_ENDED, NGAW.provider, null, model]); | |
NGAW.getAuthenticationStatus(true); | |
return; | |
} | |
} else { | |
if (userId) { | |
$NextGen.trigger(NextGen.events.METRIC_TRACKER_EVENT, [NextGen.events.AUTHENTICATION_ENDED, NGAW.provider, null, model]); | |
NGAW.getAuthenticationStatus(true); | |
return; | |
} | |
} | |
NGAW.startPollingForUserID(); | |
}, 250); | |
}, | |
/** | |
* stopPollingForUserID | |
* | |
* Cancel the timer and close the auth popup window | |
*/ | |
stopPollingForUserID: function () { | |
window.clearTimeout(NGAW.authenticationTimer); | |
}, | |
/** | |
* authenticateWithNextGenService | |
* | |
* Authentication with NextGenServices consolidation | |
* If "Anonymous" then new Services need to "link-up" here | |
*/ | |
authenticateWithNextGenService: function (provider) { | |
var url = NGAW.getProviderUrl(provider); | |
var title = "ngAuthWindow"; | |
var props = "status=1,resizable=1,scrollbars=1,height=500,width=800"; | |
if (url) { | |
NGAW.authenticationWindow = window.open(url, title, props); | |
NGAW.startPollingForUserID(); | |
} | |
}, | |
/** | |
* setProperties | |
* | |
* Set authentication user properties on NGAW object | |
*/ | |
setProperties: function () { | |
NGAW.userId = TPA.getUserId(); | |
NGAW.accessToken = TPA.getAccessToken(); | |
NGAW.provider = TPA.getProvider() || "ANONYMOUS"; | |
NGAW.planType = AXZ.getPlanTypeForDisplay(); | |
if (NGAW.userId) { | |
NGAW.setRegisteredCookie(); | |
} | |
}, | |
/** | |
* logoutUser | |
*/ | |
logoutUser: function () { | |
TPA.logoutUser(); | |
NGAW.setProperties(); | |
$NextGen.trigger(NextGen.events.AUTHENTICATION_LOGOUT_EVENT); | |
}, | |
/** | |
* setRegisteredCookie | |
*/ | |
setRegisteredCookie: function () { | |
var ONE_YEAR = 1000 * 60 * 60 * 24 * 365; | |
ngbs.u.cookie.set("save-registered", 1, { | |
domain: "." + NextGen.make.toLowerCase() + ".com", | |
expires: ONE_YEAR, | |
path: "/" | |
}); | |
}, | |
/** | |
* validateUser(caller) | |
* | |
* Entry point into Authnetication Widget. | |
* caller - Caller Widget e.g. MSI | |
*/ | |
validateUser: function (caller, lc) { | |
var concierge = lc || false; | |
NGAW.setProperties(); | |
NGAW.callerWidget = caller; | |
if (NGAW.isUpgraded) { | |
NGAW.updateAuthScreen(false); | |
} | |
var model = NGSW.saveData ? NGSW.saveData.model : ""; | |
if (NGAW.userId) { | |
NGAW.setRegisteredCookie(); | |
$NextGen.trigger(NextGen.events.AUTHENTICATION_COMPLETE_EVENT, [true, NGAW.userId, NGAW.accessToken, NGAW.provider, NGAW.callerWidget, concierge]); | |
} else { | |
NGPOP.open("Sign In", "#FNASW_authentication_content"); | |
$NextGen.trigger(NextGen.events.METRIC_TRACKER_EVENT, [NextGen.events.AUTHENTICATION_STARTED, null, null, model]); | |
} | |
}, | |
/** | |
* upgradeUser | |
* | |
* For upgrading anonymous user to a normal one | |
*/ | |
upgradeUser: function () { | |
NGAW.updateAuthScreen(true); | |
NGAW.callerWidget = NextGen.widgets.UPGRADE_USER; | |
NGPOP.open("Sign In", "#FNASW_authentication_content"); | |
}, | |
/*Function added for updating the authentication screen elemensts while upgrade*/ | |
updateAuthScreen: function (can_hide) { | |
if (can_hide) { | |
NGAW.contentAfterIcon.hide(); | |
NGAW.divider.hide(); | |
NGAW.isUpgraded = true; | |
} else { | |
NGAW.contentAfterIcon.show(); | |
NGAW.divider.show(); | |
NGAW.isUpgraded = false; | |
} | |
}, | |
/** | |
* getAuthenticationStatus | |
* | |
* For upgrading anonymous user to a normal one | |
*/ | |
getAuthenticationStatus: function (isAuthenticated) { | |
NGAW.setProperties(); | |
NGAW.stopPollingForUserID(); | |
if (isAuthenticated) { | |
NGPOP.close(); | |
$NextGen.trigger(NextGen.events.AUTHENTICATION_COMPLETE_EVENT, [true, NGAW.userId, NGAW.accessToken, NGAW.provider, NGAW.callerWidget]); | |
} | |
} | |
}; | |
/* return public properties */ | |
NGAW = new NGAW(); | |
return NGAW; | |
}()); | |
/** | |
* Save Popup Window display | |
*/ | |
NextGen.SavePopupWidget = (function () { | |
var TPA; | |
var NGDCW; | |
/* begin module */ | |
var NGPOP = function () { | |
this.visible = false; | |
this.$container = null; | |
this.$background = null; | |
this.$screens = null; | |
this.$headingTitle = null; | |
}; | |
/* add methods */ | |
NGPOP.prototype = { | |
init: function () { | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
TPA = new window.ThirdPartyAuthenticator(); | |
NGDCW = NextGen.DataCollectionWidget; | |
NGPOP.$container = $("#nextgen_auth_save_widget_container"); | |
NGPOP.$background = $("#nextgen_auth_save_container_background"); | |
NGPOP.$screens = NGPOP.$container.find(".changable_content"); | |
NGPOP.$headingTitle = $("#FNASW_window_title"); | |
NGPOP.bindHandlers(); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
$(document).delegate("#ng_popup_close_btn", "click", NGPOP.close); | |
$(document).delegate("#nextgen_auth_save_container_background", "click", NGPOP.close); | |
}, | |
setWindowHeading: function (headingText) { | |
NGPOP.$headingTitle.html(headingText); | |
}, | |
hideAllWindows: function () { | |
NGPOP.$screens.hide(); | |
}, | |
open: function (title, contentId) { | |
NGPOP.setWindowHeading(title); | |
NGPOP.hideAllWindows(); | |
NGPOP.$container.find(contentId).show(); | |
if (!NGPOP.visible) { | |
NGPOP.$background.show(); | |
NGPOP.$container.show(); | |
NGPOP.visible = true; | |
} | |
}, | |
close: function () { | |
if (NGPOP.visible) { | |
NGPOP.$background.hide(); | |
NGPOP.$container.hide(); | |
NGPOP.visible = false; | |
} | |
} | |
}; | |
/* return public properties */ | |
NGPOP = new NGPOP(); | |
return NGPOP; | |
}()); | |
/** | |
* Fallback display | |
*/ | |
NextGen.FallbackWidget = (function () { | |
/* begin module */ | |
var NGFBW = function () {}; | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
var TPA = new window.ThirdPartyAuthenticator(); | |
/* add methods */ | |
NGFBW.prototype = { | |
init: function () { | |
window.resizeTo(800, 500); | |
NGFBW.bindHandlers(); | |
}, | |
hideConciergeButton: function () { | |
var prov = TPA.getProvider() || "ANONYMOUS"; | |
var authenticatedDomain = ngbs.u.cookie.get('authenticatedDomain'); | |
var fallbackPageDomain = $('#baseurl').data("baseurl"); | |
if ((fallbackPageDomain.toLowerCase().indexOf(authenticatedDomain) < 0) || (prov === "ANONYMOUS")) { | |
$(document).find("#fallback_goto_concierge_btn").css('display', 'none'); | |
$(document).find("#fallback_ok_btn").css('margin-left', '97px'); | |
return false; | |
} | |
}, | |
bindHandlers: function () { | |
$(document).delegate("#fallback_ok_btn", "click", NGFBW.closeWindow); | |
$(document).delegate("#fallback_goto_concierge_btn", "click", NGFBW.openconcierge); | |
}, | |
openconcierge: function () { | |
window.opener.document.location.href = $('#baseurl').data("baseurl") + '/concierge'; | |
window.close(); | |
}, | |
closeWindow: function () { | |
window.open("", "_self", ""); | |
window.close(); | |
} | |
}; | |
/* return public properties */ | |
NGFBW = new NGFBW(); | |
return NGFBW; | |
}()); | |
/** | |
* Base Class NextGenSaveWidget | |
* Core SAVE Widget starts | |
*/ | |
NextGen.SaveWidget = (function () { | |
var NGAW; | |
var NGSAPI; | |
var NGPOP; | |
var NGDCW; | |
var AXZ; | |
var TPA; | |
var disclaimerRegex = /<sup\\?>(\d+|[\u2020\u2021]+|†)<\/sup\\?>/gi; | |
var htmlTagRegex = /<\/?\w+\s*\/?\\?>/gi; | |
var strip = function (str) { | |
if (str) { | |
str = str.replace(disclaimerRegex, ""); // disclaimers | |
str = str.replace(htmlTagRegex, ""); // html tags | |
return str; | |
} | |
}; | |
var slashUnescape = function (str) { | |
if (str) { | |
str = str.replace(/\\/gi, ""); | |
return str; | |
} | |
}; | |
var ellipsis = "%E2%80%A6"; | |
var ELLIPSIS_LENGTH = ellipsis.length; | |
var TITLE_MAX_LENGTH = 200 - ELLIPSIS_LENGTH; | |
var NOTE_MAX_LENGTH = 500 - ELLIPSIS_LENGTH; | |
var DESCRIPTION_MAX_LENGTH = 200 - ELLIPSIS_LENGTH; | |
var notesDefaultInput = "Add a note..."; | |
var notesDefaultHeight = 22 + "px"; | |
var encodeAndTruncate = function (str, maxLength) { | |
var lastIndex; | |
// encode only the necessary characters | |
/*jslint regexp:true*/ | |
str = str.replace(/[^a-z0-9\s,\.\|]/gi, function (str) { | |
return encodeURIComponent(str); | |
}); | |
// add ellipsis if more that max length | |
if (str.length > maxLength) { | |
str = str.slice(0, maxLength); | |
lastIndex = str.lastIndexOf(" "); | |
if (lastIndex > -1) { | |
str = str.slice(0, lastIndex); | |
} | |
str = str + ellipsis; | |
} | |
return str; | |
}; | |
/* begin module */ | |
var NGSW = function () { | |
this.saveData = null; | |
}; | |
/* add methods */ | |
NGSW.prototype = { | |
/** | |
* init | |
*/ | |
init: function () { | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
NGAW = NextGen.AuthenticationWidget; | |
NGSAPI = NextGen.SaveAPI; | |
NGPOP = NextGen.SavePopupWidget; | |
NGDCW = NextGen.DataCollectionWidget; | |
AXZ = new window.AXZAuthenticator(); | |
TPA = new window.ThirdPartyAuthenticator(); | |
NGSW.planType = AXZ.getPlanTypeForServices(); | |
NGSW.bindHandlers(); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
$NextGen.bind(NextGen.events.AUTHENTICATION_COMPLETE_EVENT, NGSW.resumeSave); | |
$(document).delegate("#submit_btn", "click", NGSW.validate); | |
$(document).delegate("#continue_btn", "click", NGPOP.close); | |
$(document).delegate("#goto_save_btn", "click", NGSW.gotoMySavedItem); | |
$(document).delegate("#cancel_btn", "click", NGPOP.close); | |
$(document).delegate("#FNASW_ng_notes", "click", NGSW.clearTextInput); | |
$(document).delegate("#FNASW_ng_notes", "keyup", NGSW.expandTextArea); | |
$(document).delegate("#concierge_continue_btn", "click", NGPOP.close); | |
$(document).delegate("#schedule_dealer_btn", "click", NGSW.scheduleDealerFlip); | |
}, | |
/** | |
* wrapper to call other vendor function | |
*/ | |
scheduleDealerFlip: function () { | |
NGPOP.close(); | |
shopOpenScheduleDealer(); | |
}, | |
/** | |
* validate | |
*/ | |
validate: function () { | |
var saveTitle = $("#FNASW_ng_save_title").val(); | |
var noteField = $("#FNASW_ng_notes").val(); | |
if (noteField === notesDefaultInput) { | |
noteField = ""; | |
} | |
if (saveTitle) { | |
NGSW.saveData.title = saveTitle.replace(/</gi, "").replace(/>/gi, ""); | |
NGSW.saveData.notes = noteField; | |
NGSW.selectServiceAndCall(NGSW.saveData); | |
} else { | |
window.alert("Title field cannot be empty"); | |
} | |
}, | |
/** | |
* clearTextInput | |
* removes default value from input | |
*/ | |
clearTextInput: function (e) { | |
var value = $(this).val(); | |
if (value === notesDefaultInput) { | |
$(this).val(""); | |
} | |
}, | |
/** | |
* expandTextArea | |
* expands textarea box for user input | |
*/ | |
expandTextArea: function (e) { | |
while ($(this).outerHeight() < this.scrollHeight + parseFloat($(this).css("borderTopWidth")) + parseFloat($(this).css("borderBottomWidth"))) { | |
if (parseInt($(this).height(), 10) >= 70) { | |
break; | |
} else { | |
$(this).height($(this).height() + 1); | |
} | |
} | |
}, | |
/** | |
* gotoMySavedItem | |
* | |
* Invoke My Saved Items from save widget | |
*/ | |
gotoMySavedItem: function () { | |
location.href = NextGen.baseURL + "/mysaveditems"; | |
}, | |
/** | |
* loadErrorScreen | |
* | |
* Function - Loads the ERROR Screen | |
*/ | |
loadErrorScreen: function () { | |
NGPOP.open("Error", "#FNASW_save_auth_error_content"); | |
}, | |
/** | |
* loadMaxItemsReachedErrorScreen | |
* | |
* Loads the maximum saved item reached ERROR Screen | |
*/ | |
loadMaxItemsReachedErrorScreen: function () { | |
var metricsData = { | |
channel: s.channel, | |
hier1: s.hier1, | |
evar11: s.eVar11 | |
}; | |
NGDCW.clickMetrics("max-saved-items", metricsData); | |
NGPOP.open("Maximum Saved Items", "#FNASW_save_max_item_error_content"); | |
}, | |
/** | |
* loadSuccessScreen | |
* | |
* Function - Loads the SUCCESS Screen | |
*/ | |
loadSuccessScreen: function () { | |
var concierge = $('#FNASW_window_title').text(); | |
if (concierge === 'Concierge') { | |
if (NGSW.saveData.thumbnailUrl !== "na") { | |
$("#FNASW_concierge_save_success_content #ng_save_item_image").attr("src", NGSW.saveData.thumbnailUrl); | |
} | |
$("#FNASW_concierge_save_success_content #FNASW_vehicle_info").html(NGSW.saveData.title); | |
if (NGSW.saveData.itemType === "config" || NGSW.saveData.itemType === "inventory_si" || NGSW.saveData.itemType === "inventory_bp") { | |
$("#FNASW_concierge_save_success_content #schedule_dealer_btn").css('display', 'block'); | |
} else { | |
$("#FNASW_concierge_save_success_content #schedule_dealer_btn").css('display', 'none'); | |
} | |
NGPOP.open("Concierge", "#FNASW_concierge_save_success_content"); | |
} else { | |
if (NGSW.saveData.thumbnailUrl !== "na") { | |
$("#FNASW_save_success_content #ng_save_item_image").attr("src", NGSW.saveData.thumbnailUrl); | |
} | |
NGPOP.open("Save An Item", "#FNASW_save_success_content"); | |
} | |
}, | |
/** | |
* loadSaveScreen | |
* | |
* Function - Loads the SAVE Form | |
*/ | |
loadSaveScreen: function (lc) { | |
var concierge = lc || false; | |
if (NGSW.saveData) { | |
$NextGen.trigger(NextGen.events.METRIC_TRACKER_EVENT, [NextGen.events.SAVE_STARTED, null, null, NGSW.saveData.model]); | |
if (NGSW.saveData.thumbnailUrl === "na") { | |
$("#ng_save_item_image").addClass("ng_save_item_hide_thumbnail"); | |
} else { | |
$("#ng_save_item_image").attr("src", NGSW.saveData.thumbnailUrl); | |
} | |
$("#FNASW_ng_notes").val(notesDefaultInput).height(notesDefaultHeight); | |
//console.dir(NGSW.saveData); | |
$("#FNASW_ng_save_title").val(NGSW.saveData.title); | |
if (concierge) { | |
NGPOP.open("Concierge", "#FNASW_save_content"); | |
} else { | |
NGPOP.open("Save An Item", "#FNASW_save_content"); | |
} | |
} | |
}, | |
/** | |
* selectServiceAndCall | |
* | |
* Selection of Respective SAVE "Services" & Invoke the "Service Caller Function" | |
*/ | |
selectServiceAndCall: function (data) { | |
var itemType = data.itemType; | |
var itemID = data.id || ""; | |
var method; | |
switch (itemType) { | |
case "config": | |
method = "createConfigVehicle"; | |
break; | |
case "inventory_bp": | |
case "inventory_si": | |
method = "createInventoryVehicle"; | |
break; | |
case "cpo": | |
method = "createCPOVehicle"; | |
break; | |
case "image": | |
case "video": | |
case "technology": | |
method = "createBrandContent"; | |
break; | |
case "compare": | |
method = "createCompareVehicle"; | |
break; | |
default: | |
break; | |
} | |
if (!method) { | |
NGSW.loadErrorScreen(); | |
return; | |
} | |
/* clone object before encoding */ | |
data = $.extend(true, {}, data); | |
/* skip these keys when encoding */ | |
var skip = ["configToken", "description", "title", "notes", "price", "competitorVehicleADescription", "competitorVehicleBDescription"]; | |
$.each(data, function (key, value) { | |
if ($.inArray(key, skip) === -1) { | |
data[key] = encodeURIComponent(value); | |
} | |
}); | |
if (data.title) { | |
data.title = encodeAndTruncate(data.title, TITLE_MAX_LENGTH); | |
} | |
if (data.notes) { | |
data.notes = encodeAndTruncate(data.notes, NOTE_MAX_LENGTH); | |
} | |
/* ajax callbacks */ | |
var saveSuccess = function (data, status, jqXHR) { | |
var statusCode = jqXHR.status; | |
var model = NGSW.saveData ? NGSW.saveData.model : ""; | |
NGSW.loadSuccessScreen(); | |
$NextGen.trigger(NextGen.events.METRIC_TRACKER_EVENT, [NextGen.events.SAVE_ENDED, null, itemType, model]); | |
}; | |
var updateSuccess = function () { | |
$(MySavedItems).trigger(MySavedItems.Events.DATA_REQUEST_START); | |
}; | |
var saveError = function (jqXHR, type, status) { | |
var statusCode = jqXHR.status; | |
if (statusCode === 403) { | |
NGSW.loadMaxItemsReachedErrorScreen(); | |
} else { | |
NGSW.loadErrorScreen(); | |
} | |
}; | |
var updateError = function () { | |
console.log("error"); | |
}; | |
/* call api method */ | |
if (itemID) { | |
NGSAPI[method](data, updateSuccess, updateError, itemID); | |
} else { | |
NGSAPI[method](data, saveSuccess, saveError); | |
} | |
}, | |
processData: function (data) { | |
/* strip html tags and disclaimers */ | |
data.title = slashUnescape(strip(data.title)); | |
data.description = slashUnescape(strip(data.description)); | |
/* special encoding and truncate */ | |
data.description = encodeAndTruncate(data.title + "||" + data.description, DESCRIPTION_MAX_LENGTH); | |
/* add postal code */ | |
var zip = ngbs.u.cookie && ngbs.u.cookie.zip && ngbs.u.cookie.zip(); | |
data.postalCode = zip || "00000"; | |
/* add user plan type */ | |
if (NGSW.planType) { | |
data.planType = NGSW.planType; | |
} | |
return data; | |
}, | |
/** | |
* loadSaveWidget | |
* | |
* Load Save Widget and perform the Authentication internally | |
*/ | |
loadSaveWidget: function (data, $target) { | |
var $conciergedata = $('#kill-switch-status'); | |
var lcswitch = $conciergedata.data('status'); | |
var make = $conciergedata.data('make').toLowerCase(); | |
var userid = TPA.getUserId(); | |
var provider = TPA.getProvider(); | |
/* save reference and pass control to validation */ | |
NGSW.saveData = NGSW.processData(data); | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
/* Integrate killswitch and swap flips for concierge */ | |
if (lcswitch === "on" && make === 'lincoln') { | |
if (userid === null) { | |
if ($target && $target.length > 0 && $target.hasClass('lc-mobile-auth-trigger')) { | |
ngbs._message_bus.send(document.body, 'open-mobile-auth', {target: $target, saveData: NGSW.saveData}); | |
} else { | |
Concierge.AuthWidget.openFlipForSave(NGSW.saveData); | |
} | |
} else { | |
if (provider === 'ANONYMOUS') { | |
NGAW.validateUser(NextGen.widgets.SAVE_WIDGET_NAME); | |
} else { // load concierge save flip | |
NGAW.validateUser(NextGen.widgets.SAVE_WIDGET_NAME, true); | |
} | |
} | |
} else { | |
NGAW.validateUser(NextGen.widgets.SAVE_WIDGET_NAME); | |
} | |
/* End of integration */ | |
}, | |
/** | |
* resumeSave | |
* | |
* Save Widget "Back to Action" after the Authentication completed | |
*/ | |
resumeSave: function (event, isAuthenticated, userIdentity, accessCode, provider, callerWidget, lc) { | |
var concierge = lc || false; | |
if (isAuthenticated && callerWidget === NextGen.widgets.SAVE_WIDGET_NAME) { | |
NGSW.loadSaveScreen(concierge); | |
} | |
} | |
}; | |
/* return public properties */ | |
NGSW = new NGSW(); | |
return NGSW; | |
}()); | |
/** | |
* Data Collection Widget | |
* To Collect Data & Invoke SAVE Widget | |
* VIEW / Pages need to invoke and pass data here | |
* Works with HTML Page . FLASH / Flex web pages | |
* do not require it | |
*/ | |
NextGen.DataCollectionWidget = (function () { | |
var NGAW; | |
var NGSW; | |
var NGPOP; | |
var TPA; | |
/* begin module */ | |
var NGDCW = function () {}; | |
/* add methods */ | |
NGDCW.prototype = { | |
/** | |
* init | |
*/ | |
init: function () { | |
if (!window.ThirdPartyAuthenticator) { | |
return; | |
} | |
TPA = new window.ThirdPartyAuthenticator(); | |
NGAW = NextGen.AuthenticationWidget; | |
NGSW = NextGen.SaveWidget; | |
NGPOP = NextGen.SavePopupWidget; | |
NGDCW.bindHandlers(); | |
NGDCW.show(); | |
NGDCW.configMetrics(); | |
}, | |
/** | |
* show | |
*/ | |
show: function () { | |
$(document.body).addClass("ng_save"); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
$NextGen.bind(NextGen.events.METRIC_TRACKER_EVENT, NGDCW.metric_tracker_handler); | |
$NextGen.bind(NextGen.events.AUTHENTICATION_COMPLETE_EVENT, NGDCW.authenticationCompleteHandler); | |
$(document).delegate("a[rel=ng_save_bookmark]", "click", NGDCW.formObjectAndInvokeSave); | |
}, | |
/** | |
* formObjectAndInvokeSave | |
* | |
* Collects Data from Anchor element & Formation of JSON Object based | |
* on SavedItemTypes = {"config","inventory_bp","inventory_si","cpo","image","video","technology"} | |
*/ | |
formObjectAndInvokeSave: function (e) { | |
e.preventDefault(); | |
var data = $(this).readDataAttr(); | |
NGSW.loadSaveWidget(data, $(this)); | |
}, | |
/** | |
* formObjectAndUpdateItem | |
* | |
* Collects Data from MySavedItems.Page._updateItemHandler dom elements data attribute. | |
*/ | |
formObjectAndUpdateItem: function (element) { | |
var data = $(element).parent().readDataAttr(); | |
data.notes = $(element).parent().siblings(".msi-note-field").val(); | |
NGSW.selectServiceAndCall(data); | |
}, | |
/** | |
* authenticationCompleteHandler | |
* | |
* Authentication complete handler that is used only for MSI and upgrade user | |
* the provider value will be - GOOGLE, YAHOO, FACEBOOK, TWITTER or ANONYMOUS | |
*/ | |
authenticationCompleteHandler: function (event, isAuthenticated, userIdentity, accessCode, provider, callerWidget) { | |
if (isAuthenticated && callerWidget === NextGen.widgets.MY_SAVED_ITEM_WIDGET_NAME) { | |
// my saved items auth complete | |
console.log(NextGen.widgets.MY_SAVED_ITEM_WIDGET_NAME); | |
} else if (isAuthenticated && callerWidget === NextGen.widgets.UPGRADE_USER) { | |
// upgrade user auth complete | |
console.log(NextGen.widgets.UPGRADE_USER); | |
} | |
}, | |
/** | |
* metric_tracker_handler | |
* | |
* Sample event handler for metric tracker | |
* @params {event} the event name | |
* @params {event_name} type of metric event happened inside the widget | |
* Possible values AUTHENTICATION_STARTED, AUTHENTICATION_ENDED, SAVE_STARTED, SAVE_ENDED | |
* @params {provider} the provider of authentication. This value will be passed only from authentication event | |
* the provider value will be - GOOGLE, YAHOO, FACEBOOK, TWITTER or ANONYMOUS | |
* @params {itemType} related to saved item metrics - required to pass the type of item successfully saved. | |
* @params {model} The model | |
*/ | |
metric_tracker_handler: function (event, event_name, provider, itemType, model) { | |
if (itemType === "config") { | |
itemType = "bp"; | |
} | |
if (typeof model === "undefined") { | |
model = ""; | |
} else if (model === "SuperDuty") { /*Fix for NG-28809, metrics model name for Super Duty*/ | |
model = "Super Duty"; | |
} | |
var metricsData = { | |
itemType: itemType, | |
channel: s.channel, | |
hier1: s.hier1, | |
evar11: s.eVar11, | |
evar16: model | |
}; | |
switch (event_name) { | |
case NextGen.events.SAVE_STARTED: | |
NGDCW.clickMetrics("saveStarted", metricsData); | |
break; | |
case NextGen.events.SAVE_ENDED: | |
NGDCW.clickMetrics("saveEnded", metricsData); | |
break; | |
case NextGen.events.AUTHENTICATION_STARTED: | |
NGDCW.clickMetrics("authStarted", metricsData); | |
break; | |
case NextGen.events.AUTHENTICATION_ENDED: | |
ngbs.metrics.constructor(); | |
var tpa = new ThirdPartyAuthenticator(); | |
var userStat = tpa.getProfileStatus(); | |
// This method tells, whether the profile requested or | |
// created already exists(-1) or succeeded(1) or failed(0). | |
if (userStat === "1") { | |
NGDCW.clickMetrics("regComplete", metricsData); | |
} else { | |
NGDCW.clickMetrics("authComplete", metricsData); | |
} | |
break; | |
default: | |
break; | |
} | |
}, | |
/** | |
* configMetrics | |
* | |
* Fires click metrics | |
* | |
*/ | |
configMetrics: function () { | |
var base; | |
var make = NextGen.make.toLowerCase(); | |
if (make === "ford") { | |
base = "fv: myfolder: "; | |
} else { | |
base = "ln: myfolder: "; | |
} | |
var getItemType = function (data) { | |
return data.itemType; | |
}; | |
var getChannel = function (data) { | |
return data.channel; | |
}; | |
var getHier1 = function (data) { | |
return data.hier1; | |
}; | |
var getEvar11 = function (data) { | |
return data.evar11; | |
}; | |
var getEvar16 = function (data) { | |
if (data.evar16) { | |
return make + ' ' + data.evar16.toLowerCase(); | |
} | |
return ""; | |
}; | |
ngbs.metrics.config({ | |
click: { | |
"saveStarted": { | |
pe: "o", | |
prop5: "myfolder: start", | |
pev2: base + "save item: start", | |
channel: getChannel, | |
hier1: getHier1, | |
prop11: getEvar11, | |
eVar11: getEvar11, | |
eVar16: getEvar16, | |
prop16: getEvar16 | |
}, | |
"saveEnded": { | |
pe: "o", | |
prop5: "myfolder: complete", | |
pev2: base + "save item: complete", | |
channel: getChannel, | |
hier1: getHier1, | |
prop11: getEvar11, | |
eVar11: getEvar11, | |
eVar16: getEvar16, | |
prop16: getEvar16, | |
prop49: getItemType, | |
prop48: "event: save content", | |
eVar49: getItemType, | |
eVar48: "event: save content", | |
events: ["event43"] | |
}, | |
"authStarted": { | |
pe: "o", | |
pev2: base + "login: start", | |
channel: getChannel, | |
hier1: getHier1, | |
prop11: getEvar11, | |
eVar11: getEvar11, | |
eVar16: getEvar16, | |
prop16: getEvar16, | |
prop42: "logged out", | |
eVar42: "logged out" | |
}, | |
"regComplete": { | |
pe: "o", | |
pev2: base + "login: complete", | |
channel: getChannel, | |
hier1: getHier1, | |
prop11: getEvar11, | |
eVar11: getEvar11, | |
eVar16: getEvar16, | |
prop16: getEvar16, | |
events: ["event14", "event47"] | |
}, | |
"authComplete": { | |
pe: "o", | |
pev2: base + "login: complete", | |
channel: getChannel, | |
hier1: getHier1, | |
prop11: getEvar11, | |
eVar11: getEvar11, | |
eVar16: getEvar16, | |
prop16: getEvar16, | |
events: ["event47"] | |
}, | |
"max-saved-items": { | |
pe: "o", | |
pev2: base + "saves exceeded", | |
channel: getChannel, | |
hier1: getHier1, | |
prop11: getEvar11, | |
eVar11: getEvar11 | |
} | |
} | |
}); | |
}, | |
/** | |
* clickMetrics | |
* | |
* Fires click metrics | |
* | |
* @param {String} clickName | |
* @param {Object} data | |
*/ | |
clickMetrics: function (clickName, data) { | |
ngbs.metrics.click(clickName, data); | |
}, | |
/** | |
* pageMetrics | |
* | |
* Fires page level metrics | |
* | |
* @param {String} pageName | |
* @param {Object} data | |
*/ | |
pageMetrics: function (pageName, data) { | |
ngbs.metrics.page(pageName, data); | |
} | |
}; | |
/* return public properties */ | |
NGDCW = new NGDCW(); | |
return NGDCW; | |
}()); | |
/** | |
* NextGen.init | |
* | |
* Initialize the save and authentication widgets | |
* @param {Object} options Base URL and Make | |
*/ | |
NextGen.init = function (options) { | |
if (NextGen.ready) { | |
return; | |
} | |
NextGen.ready = true; | |
NextGen.baseURL = options.baseURL; | |
NextGen.make = options.make; | |
NextGen.SaveAPI.init(); | |
NextGen.AuthenticationWidget.init(); | |
NextGen.SavePopupWidget.init(); | |
NextGen.SaveWidget.init(); | |
NextGen.DataCollectionWidget.init(); | |
}; | |
$(document).ready(function () { | |
var $widget = null; | |
var baseURL; | |
var make; | |
// check for auth widget until it is loaded | |
var interval = window.setInterval(function () { | |
$widget = $("#nextgen_auth_save_widget_container"); | |
if ($widget.length) { | |
window.clearInterval(interval); | |
baseURL = $widget.data("baseUrl"); | |
make = $widget.data("make"); | |
NextGen.init({ | |
baseURL: baseURL, | |
make: make | |
}); | |
ngbs._message_bus.listen(document.body, 'lc-init', function () { | |
ngbs._message_bus.send(document.body, 'nextgen-loaded'); | |
}); | |
} | |
}, 100); | |
if ($("#fallback_ok_btn").length) { | |
NextGen.FallbackWidget.init(); | |
NextGen.FallbackWidget.hideConciergeButton(); | |
} | |
}); | |
}(window, jQuery, ngbs)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global ngbs*/ | |
(function (ngbs) { | |
'use strict'; | |
if (!ngbs.u) { | |
return; | |
} | |
/** | |
* @class ngbs.u.anchor | |
* ngbs.u.anchor - A URL parsing utility | |
* | |
* @usage | |
* var a = ngbs.u.anchor("http://www.example.com?fruit=banana&pie=apple#page=1"); | |
* a.href(); // "http://www.example.com?fruit=banana&pie=apple#page=1"; | |
* a.search(); // "?fruit=banana&pie=apple" | |
* a.hash(); // "#page=1" | |
* | |
* a.getSearchVars(); // {"fruit": "banana", "pie": "apple"} | |
* a.getHashVars(); // {"page": "1"} | |
* | |
* a.setSearchVars({fruit: "pear", type: "test"}); | |
* a.href(); // "http://www.example.com/?fruit=pear&pie=apple&type=test#page=1" | |
* | |
* a.setHashVars({page: 2}); | |
* a.href(); // "http://www.example.com/?fruit=pear&pie=apple&type=test#page=2" | |
* | |
* a.setSearchVar("type", "live"); | |
* a.href(); // "http://www.example.com/?fruit=pear&pie=apple&type=live#page=2" | |
* | |
* a.setHashVar("page", 3); | |
* a.href(); // "http://www.example.com/?fruit=pear&pie=apple&type=live#page=3" | |
* | |
* a.delSearchVar("type"); | |
* a.href(); // "http://www.example.com/?fruit=pear&pie=apple#page=3" | |
* | |
* a.delHashVar("page"); | |
* a.href(); // "http://www.example.com/?fruit=pear&pie=apple#" | |
* | |
* var q = ngbs.u.getQuery(); // Duplicates functionality of ngbs.u.getQuery | |
*/ | |
ngbs.u.anchor = (function (window, document, location) { | |
/** | |
* isObject | |
* Type check | |
* @private | |
* @return {Boolean} | |
*/ | |
var isObject = function (o) { | |
return typeof o === "object" && o !== null; | |
}; | |
/** | |
* isArray | |
* Type check | |
* @private | |
* @return {Boolean} | |
*/ | |
var isArray = function (o) { | |
return Object.prototype.toString.call(o) === "[object Array]"; | |
}; | |
/** | |
* extend | |
* Object extend | |
* @private | |
* @return {Object} | |
*/ | |
var extend = function (o, o2) { | |
var args = Array.prototype.slice.call(arguments, 2), | |
i; | |
for (i in o2) { | |
if (o2.hasOwnProperty(i)) { | |
o[i] = o2[i]; | |
} | |
} | |
if (args.length) { | |
args.unshift(o); | |
extend.apply(this, args); | |
} | |
return o; | |
}; | |
/** | |
* append | |
* Append a key-value pair to an object | |
* @private | |
*/ | |
var append = function (o, key, val) { | |
if (isObject(o) && o.hasOwnProperty(key)) { | |
if (isArray(o[key])) { | |
o[key].push(val); | |
} else { | |
o[key] = [o[key], val]; | |
} | |
} else { | |
o[key] = val; | |
} | |
}; | |
/** | |
* stringify | |
* Turn a key value pair into a string | |
* @private | |
* @return {String} | |
*/ | |
var stringify = function (key, val, eq, sep) { | |
var i, l, s = ""; | |
if (isArray(val)) { | |
for (i = 0, l = val.length; i < l; i++) { | |
s += sep + key + eq + val[i]; | |
} | |
} else { | |
s += sep + key + eq + val; | |
} | |
return s; | |
}; | |
/** | |
* update | |
* Update the value of a key | |
* @private | |
*/ | |
var update = function (o, key, val) { | |
if (isObject(o)) { | |
if (typeof val === "undefined") { | |
delete o[key]; | |
} else { | |
o[key] = val; | |
} | |
} | |
}; | |
/** | |
* Private methods to add to the Anchor prototype | |
* @lends Anchor | |
* @private | |
*/ | |
var privateMethods = { | |
/** | |
* toObject_ | |
* Turn a search or hash into an object | |
* @private | |
* @param {String} type Either "search" or "hash" | |
* @return {Object} | |
*/ | |
toObject_: function (type) { | |
var str, eq, sep, list, map, i, l, pair; | |
if (type === "search") { | |
str = this.anchor.search.replace(/^\?/, ""); | |
eq = this.sass; | |
sep = this.ssep; | |
} else { | |
str = this.anchor.hash.replace(/^\#/, ""); | |
eq = this.hass; | |
sep = this.hsep; | |
} | |
list = str.split(sep); | |
map = {}; | |
for (i = 0, l = list.length; i < l; i++) { | |
pair = list[i].split(eq); | |
if (pair[0] !== "") { | |
append(map, pair[0], pair[1]); | |
} | |
} | |
return map; | |
}, | |
/** | |
* toString_ | |
* Turn an object into a str for search or hash | |
* @private | |
* @param {String} type Either "search" or "hash" | |
* @param {Object} map Key-value map of parameters | |
* @return {String} | |
*/ | |
toString_: function (type, map) { | |
var str = "", eq, sep, i; | |
if (type === "search") { | |
eq = this.sass; | |
sep = this.ssep; | |
} else { | |
eq = this.hass; | |
sep = this.hsep; | |
} | |
for (i in map) { | |
if (map.hasOwnProperty(i)) { | |
str += stringify(i, map[i], eq, sep); | |
} | |
} | |
return str.replace(new RegExp("^\\" + sep), ""); | |
}, | |
/** | |
* getUrlVars_ | |
* Get search and hash vars | |
* @private | |
* @param {String} type Either "search" or "hash" | |
* @return {Object} | |
*/ | |
getUrlVars_: function (type) { | |
var vars; | |
vars = { | |
search: this.toObject_("search"), | |
hash: this.toObject_("hash") | |
}; | |
return type ? vars[type] : vars; | |
}, | |
/** | |
* setUrlVars_ | |
* Set search and hash vars | |
* @private | |
* @param {String} type Either "search" or "hash" | |
* @param {Object} map Key-value map of parameters | |
* @return {Anchor} | |
*/ | |
setUrlVars_: function (type, map) { | |
var vars = this.getUrlVars_(type), | |
i; | |
for (i in map) { | |
if (map.hasOwnProperty(i)) { | |
update(vars, i, map[i]); | |
} | |
} | |
return this.toString_(type, vars); | |
} | |
}; | |
/** | |
* Public methods to add to the Anchor prototype | |
* @lends Anchor | |
* @ignore | |
*/ | |
var publicMethods = { | |
/** | |
* getSearchVars | |
* Return a key-value object with the parameters in the URL search | |
* @return {Object} | |
*/ | |
getSearchVars: function () { | |
return this.getUrlVars_("search"); | |
}, | |
/** | |
* getHashVars | |
* Return a key-value object with the parameters in the URL hash | |
* @return {Object} | |
*/ | |
getHashVars: function () { | |
return this.getUrlVars_("hash"); | |
}, | |
/** | |
* getUrlVars | |
* Return combined url variables | |
* @return {Object} | |
*/ | |
getUrlVars: function () { | |
var vars = this.getUrlVars_(), | |
search = vars.search, | |
hash = vars.hash, | |
combined = extend(search, hash); | |
return combined; | |
}, | |
/** | |
* setSearchVars | |
* Sets parameters using a key-value object in the URL search; returns this | |
* @param {Object} map The key-value object | |
* @return {Anchor} | |
*/ | |
setSearchVars: function (map) { | |
this.anchor.search = this.setUrlVars_("search", map); | |
return this; | |
}, | |
/** | |
* setSearchVar | |
* Sets the key parameter to val in the URL search; returns this | |
* @param {String} key The name of the parameter | |
* @param {String} val The value of the parameter | |
* @return {Anchor} | |
*/ | |
setSearchVar: function (key, val) { | |
var o = {}; | |
o[key] = val; | |
return this.setSearchVars(o); | |
}, | |
/** | |
* setHashVars | |
* Sets parameters using a key-value object in the URL hash; returns this | |
* @param {Object} map The key-value object | |
* @return {Anchor} | |
*/ | |
setHashVars: function (map) { | |
this.anchor.hash = this.setUrlVars_("hash", map); | |
return this; | |
}, | |
/** | |
* setHashVar | |
* Sets the key parameter to val in the URL hash; returns this | |
* @param {String} key The name of the parameter | |
* @param {String} val The value of the parameter | |
* @return {Anchor} | |
*/ | |
setHashVar: function (key, val) { | |
var o = {}; | |
o[key] = val; | |
return this.setHashVars(o); | |
}, | |
/** | |
* delSearchVar | |
* Deletes the key parameter from the URL search; returns this | |
* @param {String} key The name of the parameter | |
* @return {Anchor} | |
*/ | |
delSearchVar: function (key) { | |
return this.setSearchVar(key); | |
}, | |
/** | |
* delHashVar | |
* Deletes the key parameter from the URL hash; returns this | |
* @param {String} key The name of the parameter | |
* @return {Anchor} | |
*/ | |
delHashVar: function (key) { | |
return this.setHashVar(key); | |
}, | |
/** | |
* toString | |
* Used for type coercion of Anchor instance to string | |
* @return {String} | |
*/ | |
toString: function () { | |
return this.anchor.href; | |
} | |
}; | |
/** | |
* nativeGetter | |
* Returns a get method that aliases a native anchor property | |
* @private | |
* @param {String} prop The property name | |
* @return {Function} | |
*/ | |
var nativeGetter = function (prop) { | |
return function () { | |
return this.anchor[prop]; | |
}; | |
}; | |
// Native get methods to add to the Anchor prototype | |
var nativeMethods = (function () { | |
var methods = {}, props, prop, i, l; | |
props = ["href", "protocol", "host", "hostname", "port", "pathname", "search", "hash"]; | |
for (i = 0, l = props.length; i < l; i++) { | |
prop = props[i]; | |
methods[prop] = nativeGetter(prop); | |
} | |
return methods; | |
}()); | |
// regular expression URL test for protocol | |
var regexP = /^(http|https|ftp):/; | |
/** | |
* @class ngbs.u.anchor | |
* @constructor | |
* Constructor and prototype | |
* @param {String} href | |
* @param {String} [searchAss] Optional search assignment operator; default of "=" | |
* @param {String} [searchSep] Optional search separator operator; default of "&" | |
* @param {String} [hashAss] Optional hash assignment operator; default of "=" | |
* @param {String} [hashSep] Optional hash separator operator; default of "&" | |
* @return {Anchor} | |
*/ | |
var Anchor = function (href, /* optional */ searchAss, searchSep, hashAss, hashSep) { | |
if (typeof href === "undefined" || href === "") { | |
throw new Error("The href argument must be defined and non-empty."); | |
} | |
this.anchor = this.a = document.createElement("a"); | |
this.anchor.href = href; | |
// this forces the anchor to fill out the full path | |
if (!regexP.test(this.anchor.protocol)) { | |
this.anchor.protocol = location.protocol; | |
} | |
if (!this.anchor.hostname) { | |
this.anchor.hostname = location.hostname; | |
} | |
this.sass = searchAss || "="; | |
this.ssep = searchSep || "&"; | |
this.hass = hashAss || "="; | |
this.hsep = hashSep || "&"; | |
}; | |
Anchor.prototype = extend({}, nativeMethods, privateMethods, publicMethods); | |
/** | |
* Anchor.factory | |
* Create an Anchor instance | |
* @return {Anchor} | |
*/ | |
Anchor.factory = function (href, /* optional */ searchAss, searchSep, hashAss, hashSep) { | |
return new Anchor(href, searchAss, searchSep, hashAss, hashSep); | |
}; | |
/** | |
* Anchor.getQuery | |
* Duplicates functionality of ngbs.u.getQuery | |
* @param {String|Array|Undefined} key The parameter name, or an array of names, or undefined for all values | |
* @return {String|Object|Boolean} | |
*/ | |
Anchor.factory.getQuery = function (key) { | |
var href = location.href, | |
t = new Anchor(href), | |
vars = t.getUrlVars(), | |
result = {}, | |
l; | |
if (typeof key === "string" && vars[key]) { | |
return vars[key]; | |
} | |
if (isArray(key)) { | |
l = key.length; | |
while (l--) { | |
result[key[l]] = false; | |
if (vars[key[l]]) { | |
result[key[l]] = vars[key[l]]; | |
} | |
} | |
return result; | |
} | |
if (typeof key === "undefined") { | |
return vars; | |
} | |
return false; | |
}; | |
// return the factory method for ngbs.u.anchor | |
return Anchor.factory; | |
}(window, document, location)); | |
ngbs.u.getQuery = function () { | |
if (window.console && window.console.warn) { | |
window.console.warn('ngbs.u.getQuery is deprecated, please use ngbs.u.anchor'); | |
} | |
ngbs.u.anchor.getQuery.apply(null, arguments); | |
}; | |
}(ngbs)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global ngbs*/ | |
/** | |
* ngbs.u.deeplink - A deeplinking utility | |
* | |
* @usage | |
* ngbs.u.deeplink.addRoute | |
* ngbs.u.deeplink.addRoutes | |
* ngbs.u.deeplink.removeRoute | |
* ngbs.u.deeplink.clearRoutes | |
* ngbs.u.deeplink.gotoRoute | |
* ngbs.u.deeplink.start | |
* ngbs.u.deeplink.stop | |
*/ | |
(function (window, ngbs) { | |
"use strict"; | |
if (!ngbs.u) { | |
return; | |
} | |
// shortcuts | |
var location = window.location; | |
var setInterval = window.setInterval; | |
var clearInterval = window.clearInterval; | |
var toString = Object.prototype.toString; | |
var slice = Array.prototype.slice; | |
// native onhashchange event check | |
var isNative = typeof window.onhashchange !== "undefined"; | |
// no operation | |
var noop = function () {}; | |
// type check | |
var isArray = function (obj) { | |
return toString.call(obj) === "[object Array]"; | |
}; | |
// type check | |
var isFunction = function (obj) { | |
return toString.call(obj) === "[object Function]"; | |
}; | |
// bind | |
if (!Function.prototype.bind) { | |
Function.prototype.bind = function (context) { | |
var fn = this; | |
var args = slice.call(arguments, 1); | |
var len = args.length; | |
return function () { | |
var args2 = slice.call(arguments, len).concat(args); | |
return fn.apply(context, args2); | |
}; | |
}; | |
} | |
/** | |
* each | |
* | |
* Apply a callback to each item in an object | |
* | |
* @param {Object} obj | |
* @param {Function} callback | |
*/ | |
var each = function (obj, callback) { | |
var i; | |
var l; | |
var ret; | |
if (isArray(obj)) { | |
for (i = 0, l = obj.length; i < l; i++) { | |
ret = callback.call(obj, i, obj[i]); | |
if (ret === false) { | |
return; | |
} | |
} | |
} else { | |
for (i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
ret = callback.call(obj, i, obj[i]); | |
if (ret === false) { | |
return; | |
} | |
} | |
} | |
} | |
}; | |
ngbs.u.deeplink = (function () { | |
// default root | |
var _defaultRoot = "#!"; | |
/** | |
* @class ngbs.u.deeplink | |
* @constructor | |
* Deeplink | |
* | |
* @param {Object} routes Optional | |
* @param {String} root Optional | |
* @param {Object} context Optional | |
*/ | |
var Deeplink = function (routes, root, context) { | |
// root route | |
this._root = root || _defaultRoot; | |
// default route | |
this._defaultRoute = { | |
"default": { | |
test: new RegExp("^" + this._root + "$"), | |
callback: noop, | |
context: window | |
} | |
}; | |
// holds all module routes | |
this.clearRoutes(); | |
if (routes) { | |
this.addRoutes(routes); | |
} | |
// hold route history | |
this._history = []; | |
// interval reference for non-native browsers | |
this._interval = null; | |
// is started | |
this._started = false; | |
// proxy | |
this.deeplink = this.deeplink.bind(this); | |
}; | |
Deeplink.prototype = { | |
/** | |
* Clear all routes from the _routes object | |
*/ | |
clearRoutes: function () { | |
this._routes = { | |
"default": this._defaultRoute["default"] | |
}; | |
}, | |
/** | |
* Convert a route string to a regular expression for matching | |
* | |
* @param {String} route | |
* @return {RegExp} | |
*/ | |
routeToTest: function (route) { | |
var prefix = "^" + this._root; | |
var suffix = "$"; | |
var test; | |
route = route.replace(/\:\w+/gi, function () { | |
return "([^/]+)"; | |
}); | |
route = route.replace(/\:\*/, function () { | |
return "(.+)"; | |
}); | |
test = new RegExp(prefix + route + suffix); | |
return test; | |
}, | |
/** | |
* Add a single route to the _routes object | |
* | |
* @param {String} route | |
* @param {Function} callback | |
* @param {Object} context | |
*/ | |
addRoute: function (route, callback, context) { | |
var test = null; | |
if (route === "default") { | |
test = this._routes["default"].test; | |
} else { | |
test = this.routeToTest(route); | |
} | |
this._routes[route] = { | |
test: test, | |
callback: callback || noop, | |
context: context || window | |
}; | |
}, | |
/** | |
* Add multiple route to the _routes object. Pass in an object | |
* where the keys are routes and the values are callbacks | |
* | |
* @param {Object} routes | |
* @param {Object} context | |
*/ | |
addRoutes: function (routes, context) { | |
var self = this; | |
each(routes, function (route, callback) { | |
self.addRoute(route, callback, context); | |
}); | |
}, | |
/** | |
* Remove a single route from the _routes object | |
* | |
* @param {String} route | |
*/ | |
removeRoute: function (route) { | |
delete this._routes[route]; | |
}, | |
/** | |
* Return an array of routes that match a hash | |
* | |
* @param {String} hash | |
* @return {Array} | |
*/ | |
matchRoutes: function (hash) { | |
var matched = []; | |
each(this._routes, function (i, route) { | |
var matches = hash.match(route.test); | |
if (matches) { | |
matched.push({ | |
hash: hash, | |
args: matches.slice(1), | |
callback: route.callback, | |
context: route.context | |
}); | |
} | |
}); | |
return matched; | |
}, | |
/** | |
* Set the location.hash to a specific route | |
*/ | |
gotoRoute: function (route) { | |
var prefix = this._root; | |
var hash; | |
if (typeof route === "undefined") { | |
hash = prefix; | |
} else if (typeof route === "number" && route < 0) { | |
this._history = this._history.slice(0, route); | |
hash = this._history[this._history.length - 1]; | |
} else { | |
hash = ""; | |
} | |
if (typeof hash === "undefined") { | |
hash = prefix + route; | |
} | |
location.hash = hash; | |
}, | |
/** | |
* When the hash changes, check for matched routes and call | |
* callbacks | |
*/ | |
deeplink: function () { | |
var hash = location.hash || this._root; | |
var routes = this.matchRoutes(hash); | |
if (!routes.length) { | |
this.gotoRoute(); | |
return; | |
} | |
this._history.push(hash); | |
each(routes, function (i, route) { | |
var args = [route.hash].concat(route.args); | |
var callback = route.callback; | |
var context = route.context; | |
callback.apply(context, args); | |
}); | |
}, | |
/** | |
* Set up the hashchange listener to call the deeplink | |
* function. Also, call the deeplink function | |
*/ | |
start: function () { | |
var self = this; | |
var current; | |
if (this._started) { | |
this.stop(); | |
} | |
this._started = true; | |
if (isNative) { | |
if (window.addEventListener) { | |
window.addEventListener("hashchange", this.deeplink, false); | |
} else { | |
window.attachEvent("onhashchange", this.deeplink); | |
} | |
} else { | |
this._interval = setInterval(function () { | |
if (current !== location.hash) { | |
current = location.hash; | |
self.deeplink(); | |
} | |
}, 100); | |
} | |
current = location.hash; | |
this.deeplink(); | |
}, | |
/** | |
* Remove the hashchange listener for the deeplink | |
* function | |
*/ | |
stop: function () { | |
if (isNative) { | |
if (window.removeEventListener) { | |
window.removeEventListener("hashchange", this.deeplink, false); | |
} else { | |
window.detachEvent("onhashchange", this.deeplink); | |
} | |
} else { | |
clearInterval(this._interval); | |
} | |
this._started = false; | |
} | |
}; | |
// return constructor | |
return function (root, routes) { | |
return new Deeplink(root, routes); | |
}; | |
}()); | |
}(window, ngbs)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment