diff --git a/index.html b/index.html index 1167d74e..54fcd157 100644 --- a/index.html +++ b/index.html @@ -100,39 +100,9 @@

Tour Options

+ - + diff --git a/js/exampletour.js b/js/exampletour.js index 5a94bed4..2797ff10 100644 --- a/js/exampletour.js +++ b/js/exampletour.js @@ -51,15 +51,16 @@ var tour = { title: 'Multi-page test', content: 'Are you ready? We\'re going to try hopping to another page and then back!!! Where we\'re going, we won\'t need roads... (Please click this link.)', target: 'secpagelink', - orientation: 'bottom', - showNextButton: false, + orientation: 'right', + yOffset: -20, + onNext: ["goTo", "secondpage.html"], multipage: true }, { title: 'Polar bears', content: 'We made it!! Polar bears are very interesting creatures.', target: 'polarbears', - orientation: 'right', + orientation: 'right' //showPrevButton: false }, { @@ -67,7 +68,8 @@ var tour = { content: 'Time to go back home... Please click this link to return to the first page.', target: 'firstpagelink', orientation: 'bottom', - showNextButton: false, + //showNextButton: false, + onNext: ['goTo', 'index.html'], //showPrevButton: false, multipage: true // this indicates that next step will be on a different page }, @@ -88,6 +90,13 @@ var tour = { scrollDuration: 500, cookieName: 'li_hs', skipIfNoElement: true, + onStart: ['printlog', 'tour-onstart: start'], + onNext: ['printlog', 'clicked next'], + onPrev: ['printlog', 'clicked prev'], + onShow: ['printlog', 'showing'], + onClose: [['printlog', 'clicked close'], ['alert', 'closing the tour']], + onEnd: ['printlog', 'end'] + /* onNext: function(tourId, idx) { var newLi, list = document.getElementById('my-list'); @@ -123,6 +132,7 @@ var tour = { onClose: function() { printLog('closing'); } + */ //i18n: { ////nextBtn: 'Forward', ////prevBtn: 'Backward', diff --git a/js/hopscotch-0.0.5.js b/js/hopscotch-0.0.5.js index 616cd34c..84e67edb 100644 --- a/js/hopscotch-0.0.5.js +++ b/js/hopscotch-0.0.5.js @@ -6,6 +6,7 @@ Sizzle = window.Sizzle || null, utils, callbacks, + helpers, winLoadHandler, winHopscotch = context[namespace], undefinedStr = 'undefined', @@ -147,15 +148,71 @@ }, /** + * Invokes a single callback represented by an array. + * Example input: ["my_fn", "arg1", 2, "arg3"] * @private */ - invokeCallbacks: function(evtType, args) { + invokeCallbackArrayHelper: function(arr) { + // Logic for a single callback + var fn; + if (utils.isArray(arr)) { + fn = helpers[arr[0]]; + if (typeof fn === 'function') { + fn.apply(this, arr.slice(1)); + } + } + }, + + /** + * Invokes one or more callbacks. Array should have at most one level of nesting. + * Example input: + * ["my_fn", "arg1", 2, "arg3"] + * [["my_fn_1", "arg1", "arg2"], ["my_fn_2", "arg2-1", "arg2-2"]] + * [["my_fn_1", "arg1", "arg2"], function() { ... }] + * @private + */ + invokeCallbackArray: function(arr) { + var i, len; + + if (utils.isArray(arr)) { + if (typeof arr[0] === 'string') { + // Assume there are no nested arrays. This is the one and only callback. + utils.invokeCallbackArrayHelper(arr); + } + else { // assume an array + for (i = 0, len = arr.length; i < len; ++i) { + utils.invokeCallback(arr[i]); + } + } + } + }, + + /** + * Helper function for invoking a callback, whether defined as a function literal + * or an array that references a registered helper function. + * @private + */ + invokeCallback: function(cb) { + if (typeof cb === 'function') { + cb(); + } + else { // assuming array + utils.invokeCallbackArray(cb); + } + }, + + /** + * @private + */ + invokeEventCallbacks: function(evtType) { var cbArr = callbacks[evtType], + callback, + fn, i, len; for (i=0, len=cbArr.length; i 0 && origStep.onNext) { - origStep.onNext(); + utils.invokeCallback(origStep.onNext); } else if (direction < 0 && origStep.onPrev) { - origStep.onPrev(); + utils.invokeCallback(origStep.onPrev); } // Tour-wide next/prev callbacks - utils.invokeCallbacks(direction > 0 ? 'next' : 'prev', [currTour.id, origStepNum]); + utils.invokeEventCallbacks(direction > 0 ? 'next' : 'prev'); if (direction > 0 && wasMultiPage) { return; @@ -1354,7 +1431,7 @@ currStepNum += direction; step = getCurrStep(); if (!utils.getStepTarget(step) && !wasMultiPage) { - utils.invokeCallbacks('error', [currTour.id, currStepNum]); + utils.invokeEventCallbacks('error'); return this.endTour(true, false); } changeStepCb.call(this, currStepNum); @@ -1509,7 +1586,7 @@ } } - utils.invokeCallbacks('start', [currTour.id, currStepNum]); + utils.invokeEventCallbacks('start'); bubble = getBubble(); bubble.hide(false); // make invisible for boundingRect calculations when opt.animate == true @@ -1521,7 +1598,7 @@ if (!utils.getStepTarget(getCurrStep())) { // First step element doesn't exist - utils.invokeCallbacks('error', [currTour.id, currStepNum]); + utils.invokeEventCallbacks('error'); if (opt.skipIfNoElement) { this.nextStep(false); } @@ -1572,14 +1649,16 @@ bubble.show(); } - if (step.onShow) { step.onShow(); } + if (step.onShow) { + utils.invokeCallback(step.onShow); + } // If we want to advance to next step when user clicks on target. if (step.nextOnTargetClick) { utils.addClickListener(targetEl, targetClickNextFn); } }); - utils.invokeCallbacks('show', [currTour.id, currStepNum]); + utils.invokeEventCallbacks('show'); if (step.multipage) { cookieVal += ':mp'; @@ -1646,7 +1725,7 @@ winHopscotch.isActive = false; if (currTour && doCallbacks) { - utils.invokeCallbacks('end', [currTour.id]); + utils.invokeEventCallbacks('end'); } winHopscotch.removeCallbacks(true); @@ -1686,12 +1765,24 @@ * @returns {Object} Hopscotch */ this.listen = function(evtType, cb, isTourCb) { - if (evtType && cb) { + if (evtType) { callbacks[evtType].push({ cb: cb, fromTour: isTourCb }); } return this; }; + this.unlisten = function(evtType, cb) { + var evtCallbacks = callbacks[evtType], + i, + len; + + for (i = 0, len = evtCallbacks.length; i < len; ++i) { + if (evtCallbacks[i] === cb) { + evtCallbacks.splice(i, 1); + } + } + }; + /** * removeCallback * @@ -1748,6 +1839,24 @@ return this; }; + /** + * registerHelper + * ============== + * Registers a helper function to be used as a callback function. + * + * @param {String} id The id of the function. + * @param {Function} id The callback function. + */ + this.registerHelper = function(id, fn) { + if (typeof id === 'string' && typeof fn === 'function') { + helpers[id] = fn; + } + }; + + this.unregisterHelper = function(id) { + helpers[id] = null; + }; + /** * setCookieName * @@ -1785,7 +1894,12 @@ * @param {Boolean} isTourOptions Should be set to true when setting options from a tour definition. */ _configure = function(options, isTourOptions) { - var bubble; + var bubble, + events = ['next', 'prev', 'start', 'end', 'show', 'error', 'close'], + eventPropName, + callbackProp, + i, + len; if (!opt) { this.resetDefaultOptions(); @@ -1797,13 +1911,16 @@ utils.extend(HopscotchI18N, options.i18n); } - this.listen('next', options.onNext, isTourOptions) - .listen('prev', options.onPrev, isTourOptions) - .listen('start', options.onStart, isTourOptions) - .listen('end', options.onEnd, isTourOptions) - .listen('show', options.onShow, isTourOptions) - .listen('error', options.onError, isTourOptions) - .listen('close', options.onClose, isTourOptions); + for (i = 0, len = events.length; i < len; ++i) { + // At this point, options[eventPropName] may have changed from an array + // to a function. + eventPropName = 'on' + events[i][0].toUpperCase() + events[i].substring(1); + if (options[eventPropName]) { + this.listen(events[i], + options[eventPropName], + isTourOptions); + } + } bubble = getBubble(); diff --git a/js/li_helpers.js b/js/li_helpers.js new file mode 100644 index 00000000..eaae2bd1 --- /dev/null +++ b/js/li_helpers.js @@ -0,0 +1,100 @@ +/* Hi JSHint! Just letting ya know that we're cool with hopscotch and all. */ +/* global hopscotch:false */ + + +/* LINKEDIN BASE HELPERS BUNDLE FOR HOPSCOTCH + * Helpers are callback methods that you can invoke from Hopscotch to + * take certain actions. Each helper must be registered with Hopscotch + * using the registerHelper method before it can be used. + * + * Helpers in this bundle are registered on each page Hopscotch is loaded. + * As such, these helpers should be generic in nature and applicable + * to a variety of tours. Helpers that are unique to a specific tour + * should be registered in a helper bundle for that tour. QuickHelp + * will load your helper bundle for you when it launches your tour. + * See docs for details on tour-specific bundles. + * + * Want to use these in a Hopscotch tour? Invoke from a tour callback + * as follows: + * + * "onNext": ["trackEvent off_to_next_step", "goTo http://linkedin.com"] + * + * Note that you can call multiple helpers in each callback. Helpers are + * invoked in order, starting with all-tour callbacks, then step-specific + * callbacks. + * + * Need help? Check out go/howtohopscotch for developer information. +*/ + + +/* General boilerplate for a helper bundle. We first double-check that + * Hopscotch exists, then for each helper we call the following: + * + * hopscotch.registerHelper("nameOfHelperMethod", function(){...}); + * + * Documentation for each helper is included below. +*/ +if(hopscotch){ + + /* Helper Method: goTo + * Sends the user off to another page. + * + * Arguments: + * loc - Where you want to go to. + * + * Example: "onNext": ["goTo", "http://linkedin.com"] + */ + hopscotch.registerHelper("goTo", function(loc){ + window.location = loc; + }); + + /* Helper Method: trackEvent + * Uses LinkedIn's WebTracking framework to log a Kafka tracking event. + * Commonly used for metrics collection. See go/tracking for details. + * + * Arguments: + * eventId - The Kafka event ID you want to log. + * + * Example: "onEnd": ["trackEvent", "hurray_we_finished_the_tour"] + */ + hopscotch.registerHelper("trackEvent", function(eventId){ + if(WebTracking){ + WebTracking.trackUserAction(eventId); + } + }); + + /* Helper Method: setFormValue + * Uses native JavaScript to set the value of a text form field. + * TODO: Once jQuery is more prevalent, we should use that instead. + * + * Arguments: + * elId - The ID of the element you want to set. + * val - The value to set the form field to. Use + for spaces. + * + * Example: "onStart": ["setFormValue, "global-search Wizard+Of+In"] + */ + hopscotch.registerHelper("setFormValue", function(elId, val){ + var el = document.getElementById(elId); + + if(val && el && (typeof el.value !== "undefined")){ + val.replace("+", " "); + el.value = val; + } + }); + + /* Helper Method: submitForm + * Uses native JavaScript to submit a form. + * + * Arguments: + * elId - The ID of the form you want to submit. + * + * Example: "onNext": ["submitForm global-search"] + */ + hopscotch.registerHelper("submitForm", function(elId){ + var el = document.getElementById(elId); + if(el && (typeof el.submit === "function")){ + el.submit(); + } + }); +} + diff --git a/js/toursetup.js b/js/toursetup.js new file mode 100644 index 00000000..5124830f --- /dev/null +++ b/js/toursetup.js @@ -0,0 +1,43 @@ +var printLog = function(str) { + var el = document.getElementById('debug-output'), + newEl = document.createElement('p'); + if (el) { + newEl.innerHTML = str; + el.appendChild(newEl); + } + else { + console.log(str); + } +}, + +doAlert = function(str) { + // worst function idea ever + alert(str); +}; + +//hopscotch.clearCookie(); +hopscotch.registerHelper("printlog", printLog); +hopscotch.registerHelper("alert", doAlert); +/* +var onStart = function(tourId) { + printLog('global start ' + tourId); +}, +onEnd = function(tourId) { + printLog('global end ' + tourId); +}, +onNext = function(tourId, stepNum) { + printLog('global next ' + tourId + ' ' + stepNum); +}, +onPrev = function(tourId, stepNum) { + printLog('global prev ' + tourId + ' ' + stepNum); +}; +hopscotch.listen('start', onStart) + .listen('end', onEnd) + .listen('next', onNext) + .listen('prev', onPrev); + */ + +hopscotch.listen('start', ['printlog', 'global start']); +//hopscotch.listen('start', function() { alert('starting'); }); +hopscotch.startTour(tour); + diff --git a/secondpage.html b/secondpage.html index 302edde1..61c4babe 100644 --- a/secondpage.html +++ b/secondpage.html @@ -14,10 +14,11 @@

Second Test Page

+ +