瀏覽代碼

HUE-1387 [core] Follow me tour plugin

Added follow me tour plugin
Enrico Berti 12 年之前
父節點
當前提交
5884c3d7d0

+ 17 - 1
desktop/core/src/desktop/templates/common_footer.html

@@ -90,6 +90,21 @@ limitations under the License.
             }
           }, 200);
         }
+
+        $.jHueTour({});
+        if ($.totalStorage("jHueTourExtras") != null) {
+          $.jHueTour({tours: $.totalStorage("jHueTourExtras")});
+        }
+        var _qs = location.search;
+        if (_qs !== undefined && _qs.indexOf("tour=") > -1) {
+          $.jHueTour(getParameterByName("tour"), 1);
+        }
+        function getParameterByName(name) {
+          name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
+          var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+                  results = regex.exec(_qs);
+          return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+        }
       });
 
       function resetPrimaryButtonsStatus() {
@@ -149,5 +164,6 @@ limitations under the License.
 
     </script>
 
-	</body>
+    <script src="/static/js/Source/jHue/available.tours.js"></script>
+  </body>
 </html>

+ 28 - 20
desktop/core/src/desktop/templates/common_header.mako

@@ -75,27 +75,34 @@ from django.utils.translation import ugettext as _
 
   <script type="text/javascript" charset="utf-8">
 
-      // jHue plugins global configuration
-      jHueFileChooserGlobals = {
-        labels:{
-          BACK:"${_('Back')}",
-          SELECT_FOLDER:"${_('Select this folder')}",
-          CREATE_FOLDER:"${_('Create folder')}",
-          FOLDER_NAME:"${_('Folder name')}",
-          CANCEL:"${_('Cancel')}",
-          FILE_NOT_FOUND:"${_('The file has not been found')}",
-          UPLOAD_FILE:"${_('Upload a file')}",
-          FAILED:"${_('Failed')}"
-        },
-        user: "${ user.username }"
-      };
-
-      jHueTableExtenderGlobals = {
-        labels:{
-          GO_TO_COLUMN: "${_('Go to column:')}",
-          PLACEHOLDER: "${_('column name...')}"
-        }
+    // jHue plugins global configuration
+    jHueFileChooserGlobals = {
+      labels: {
+        BACK: "${_('Back')}",
+        SELECT_FOLDER: "${_('Select this folder')}",
+        CREATE_FOLDER: "${_('Create folder')}",
+        FOLDER_NAME: "${_('Folder name')}",
+        CANCEL: "${_('Cancel')}",
+        FILE_NOT_FOUND: "${_('The file has not been found')}",
+        UPLOAD_FILE: "${_('Upload a file')}",
+        FAILED: "${_('Failed')}"
+      },
+      user: "${ user.username }"
+    };
+
+    jHueTableExtenderGlobals = {
+      labels: {
+        GO_TO_COLUMN: "${_('Go to column:')}",
+        PLACEHOLDER: "${_('column name...')}"
+      }
+    };
+
+    jHueTourGlobals = {
+      labels: {
+        AVAILABLE_TOURS: "${_('Available tours')}",
+        NO_AVAILABLE_TOURS: "${_('None for this page.')}"
       }
+    };
 
   </script>
 
@@ -109,6 +116,7 @@ from django.utils.translation import ugettext as _
   <script src="/static/js/Source/jHue/jquery.tablescroller.js"></script>
   <script src="/static/js/Source/jHue/jquery.tableextender.js"></script>
   <script src="/static/js/Source/jHue/jquery.scrollup.js"></script>
+  <script src="/static/js/Source/jHue/jquery.tour.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.cookie.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.total-storage.min.js"></script>
   <script src="/static/ext/js/jquery/plugins/jquery.placeholder.min.js"></script>

+ 81 - 1
desktop/core/static/css/hue2.css

@@ -1224,4 +1224,84 @@ a#advanced-btn:hover {
   text-decoration: none;
   opacity: 0.85;
   filter: alpha(opacity=85);
-}
+}
+
+#jHueTourMask {
+  position: absolute;
+  display: none;
+  top: 0px;
+  left: 0px;
+  z-index: 9998;
+  background-color: #FFFFFF;
+  opacity: 0.9;
+  filter: alpha(opacity=90);
+}
+
+.jHueTourBadge {
+  background-color: #2c7fb3;
+  background-image: -moz-linear-gradient(bottom, #2c7fb3, #01639c);
+  background-image: -ms-linear-gradient(bottom, #2c7fb3, #01639c);
+  background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#2c7fb3), to(#01639c));
+  background-image: -webkit-linear-gradient(bottom, #2c7fb3, #01639c);
+  background-image: -o-linear-gradient(bottom, #2c7fb3, #01639c);
+  background-image: linear-gradient(bottom, #2c7fb3, #01639c);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#01639c', endColorstr='#2c7fb3', GradientType=0);
+  border: 1px solid #01639C;
+  border-right: none;
+  color: #FFFFFF;
+  padding: 4px;
+  padding-left: 8px;
+  -webkit-border-top-left-radius: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+  -moz-border-radius-topleft: 4px;
+  -moz-border-radius-bottomleft: 4px;
+  border-top-left-radius: 4px;
+  border-bottom-left-radius: 4px;
+  position: fixed;
+  top: 100px;
+  z-index: 10000;
+  right: 0;
+  cursor: pointer;
+}
+
+.jHueTourBadgeLeft {
+  right: auto!important;
+  left: 0;
+  padding-left: 4px;
+  padding-right: 8px;
+  border-left: none;
+  border-right: 1px solid #01639C;
+  -webkit-border-top-left-radius: 0px;
+  -webkit-border-bottom-left-radius: 0px;
+  -moz-border-radius-topleft: 0px;
+  -moz-border-radius-bottomleft: 0px;
+  border-top-left-radius: 0px;
+  border-bottom-left-radius: 0px;
+  -webkit-border-top-right-radius: 4px;
+  -webkit-border-bottom-right-radius: 4px;
+  -moz-border-radius-topright: 4px;
+  -moz-border-radius-bottomright: 4px;
+  border-top-right-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+
+.jHueTourBadge:hover {
+  background-color: #2C7FB3;
+}
+
+.jHueTourExposed {
+  z-index: 9999;
+}
+
+.popover-inner {
+  text-align: left;
+}
+
+.popover {
+  z-index: 10000;
+}
+
+.CodeMirror-hints {
+  z-index: 10000 !important;
+}

+ 18 - 0
desktop/core/static/js/Source/jHue/available.tours.js

@@ -0,0 +1,18 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+// put the tours here

+ 463 - 0
desktop/core/static/js/Source/jHue/jquery.tour.js

@@ -0,0 +1,463 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+/*
+ * jHue tour plugin
+ * Optionally depends on $.totalstorage for progress checking and $.jHueNotify for error notification
+ * Can be instantiated with
+ $.jHueTour({
+        tours: [  <-- array, the tours available for this page
+          {
+            name: "xxxx", <-- unique tour name  (location.pathname scope)
+            desc: "Desc yyyy", <-- the label shown on the question mark
+            path: "beeswax/*", <-- string for the path to show this tour on
+            steps: [ <-- array, steps of the tour
+              {
+                arrowOn: "a[href='/beeswax']", <-- the element relative to the popover is positioned
+                expose: ".navbar-fixed-top", <-- optional, the exposed object. if not present, arrowOn will be exposed
+                title: "Welcome to Beeswax!", <-- popover title
+                content: "This is a tour of the Beeswax app. <br/><b>HTML</b> is supported <em>too!</em>", <-- popover content, html enable
+                placement: "bottom", <-- popover placement
+                left: "100px", <-- popover absolute position (css string)
+                top: -20 <-- popover relative position (it adds that amount of pixels to the popover calculated position)
+                visitUrl: "blabla?tour=hello" <-- overrides everything, redirects to specific url
+              },
+              {
+                arrowOn: ".subnav-fixed",
+                title: "Beeswax sections",
+                content: "There are several sections in the Beeswax app",
+                placement: "bottom",
+                left: "100px"
+              }, ...
+            ]
+          }, ...
+        ]
+      });
+
+  Calling $.jHueTour({tours: [...]}) more than once will merge the tour data, so you can keep adding tours dinamically to the same page
+
+  You can interact with:
+  - $.jHueTour("start") / $.jHueTour("show") / $.jHueTour("play") : starts the first available tour
+  - $.jHueTour("stop") / $.jHueTour("close") / $.jHueTour("hide") / $.jHueTour("stop") : starts the first available tour
+  - $.jHueTour("reset") : removes stored tours and history
+  - $.jHueTour("clear") : removes current tours
+  - $.jHueTour("http://remote/hue/tour.hue") : loads a remote tour
+  - $.jHueTour("tourName", 1) : loads tour name and start at step 1
+  - $.jHueTour() : returns the available tours
+ */
+
+(function ($, window, document, undefined) {
+
+  var pluginName = "jHueTour",
+    defaults = {
+      labels: {
+        AVAILABLE_TOURS: "Available tours",
+        NO_AVAILABLE_TOURS: "None for this page"
+      },
+      tours: [],
+      showRemote: false,
+      placement: "right"
+    };
+
+  function Plugin(element, options) {
+    this.element = element;
+    if (typeof jHueTourGlobals !== undefined) {
+      var extendedDefaults = $.extend({}, defaults, jHueTourGlobals);
+      extendedDefaults.labels = $.extend({}, defaults.labels, jHueTourGlobals.labels);
+      this.options = $.extend({}, extendedDefaults, options);
+      this.options = $.extend({}, defaults, this.options);
+    }
+    else {
+      this.options = $.extend({}, defaults, options);
+    }
+    this._defaults = defaults;
+    this._name = pluginName;
+    this.currentTour = {
+      name: "",
+      path: "",
+      remote: false,
+      steps: [],
+      shownStep: 0
+    };
+    this.init();
+  }
+
+  Plugin.prototype.init = function () {
+    var _this = this;
+    _this.initQuestionMark();
+    var _tourMask = $("<div>").attr("id", "jHueTourMask");
+    _tourMask.addClass("jHueTourBadge");
+    if (_this.options.placement == "left"){
+      _tourMask.addClass("jHueTourBadgeLeft");
+    }
+    _tourMask.width($(document).width()).height($(document).height())
+    _tourMask.click(function () {
+      _this.closeCurtains();
+    });
+    _tourMask.appendTo($("body"));
+
+    $(document).on("keyup", function (e) {
+      var _code = (e.keyCode ? e.keyCode : e.which);
+      if ($("#jHueTourMask").is(":visible") && _code == 27) {
+        _this.performOperation("close");
+      }
+    });
+  };
+
+  Plugin.prototype.initQuestionMark = function () {
+    var _this = this;
+    $("#jHueTourQuestion").remove();
+    var _questionMark = $("<div>").attr("id", "jHueTourQuestion").html('<i class="icon-question"></i>').addClass("jHueTourBadge");
+    _questionMark.click(function () {
+      if ($.totalStorage("jHueTourExtras") != null) {
+        var _newTours = [];
+        $.each(_this.options.tours, function (cnt, tour) {
+          if (tour.remote == undefined || !tour.remote) {
+            _newTours.push(tour);
+          }
+        });
+        _this.options.tours = _newTours.concat($.totalStorage("jHueTourExtras"));
+      }
+
+      var _closeBtn = $("<a>");
+      _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
+      _closeBtn.click(function () {
+        $(".popover").remove();
+      });
+
+      var _toursHtml = '<ul class="nav nav-pills nav-stacked">'
+      var _added = 0;
+      $.each(_this.options.tours, function (ctn, tour) {
+        if (tour.path === undefined || RegExp(tour.path).test(location.pathname)) {
+          var _tourDone = '';
+          var _removeTour = '';
+          if ($.totalStorage !== undefined) {
+            var _key = location.pathname;
+            if (tour.path !== undefined && tour.path != "") {
+              _key = tour.path;
+            }
+            _key += "_" + tour.name;
+            if ($.totalStorage("jHueTourHistory") != null && $.totalStorage("jHueTourHistory")[_key] == true) {
+              _tourDone = '<div style="color:green;float:right;margin:4px"><i class="icon-check-sign"></i></div>';
+            }
+          }
+          if (tour.remote) {
+            _removeTour = '<div style="color:red;float:right;margin:4px;cursor: pointer" onclick="javascript:$.jHueTour(\'remove_' + tour.name + '\')"><i class="icon-remove-sign"></i></div>';
+          }
+          _toursHtml += '<li>' + _removeTour + _tourDone + '<a href="javascript:$.jHueTour(\'' + tour.name + '\', 1)" style="padding-left:0">' + tour.desc + '</a></li>';
+          _added++;
+        }
+      });
+      if (_added == 0) {
+        _toursHtml += '<li>' + _this.options.labels.NO_AVAILABLE_TOURS + '</li>';
+      }
+      if (_this.options.showRemote){
+        _toursHtml += '<li>' +
+          ' <div class="input-append" style="margin-top: 10px">' +
+          '  <input id="jHueTourRemoteTutorial" style="width:70%" type="text" placeholder="URL">' +
+          '  <button id="jHueTourRemoteTutorialBtn" class="btn" type="button" onclick="javascript:$.jHueTour($(\'#jHueTourRemoteTutorial\').val())">' +
+          '  <i class="icon-cloud-download"></i></button>' +
+          ' </div>' +
+          '</li>';
+      }
+      _toursHtml += '</ul>';
+
+      _questionMark.popover("destroy").popover({
+        title: _this.options.labels.AVAILABLE_TOURS,
+        content: _toursHtml,
+        html: true,
+        trigger: "manual",
+        placement: "left"
+      }).popover("show");
+      if ($(".popover").position().top <= 0) {
+        $(".popover").css("top", "10px");
+      }
+      _closeBtn.prependTo($(".popover-title"));
+    });
+    _questionMark.appendTo($("body"));
+  };
+
+  Plugin.prototype.addTours = function (options) {
+    var _this = this;
+    var _addableTours = [];
+    if (options.tours != null) {
+      $.each(options.tours, function (cnt, tour) {
+        var _add = true;
+        if (_this.options.tours != null) {
+          $.each(_this.options.tours, function (icnt, itour) {
+            if (itour.name == tour.name) {
+              _add = false;
+            }
+          });
+        }
+        if (_add) {
+          _addableTours.push(tour);
+        }
+      });
+    }
+    _this.options.tours = _this.options.tours.concat(_addableTours);
+  };
+
+  Plugin.prototype.availableTours = function () {
+    return this.options.tours;
+  };
+
+  Plugin.prototype.performOperation = function (operation) {
+    var _this = this;
+    var _op = operation.toLowerCase();
+    if (_op.indexOf("http:") == 0) {
+      $("#jHueTourRemoteTutorial").attr("disabled", "disabled");
+      $("#jHueTourRemoteTutorialBtn").attr("disabled", "disabled");
+      $.ajax({
+        type: "GET",
+        url: operation + "?callback=?",
+        async: false,
+        jsonpCallback: "jHueRemoteTour",
+        contentType: "application/json",
+        dataType: "jsonp",
+        success: function (json) {
+          if ($.totalStorage !== undefined) {
+            if ($.totalStorage("jHueTourExtras") == null) {
+              $.totalStorage("jHueTourExtras", []);
+            }
+            var _newStoredArray = [];
+            if (json.tours != null) {
+              _newStoredArray = json.tours;
+              $.each($.totalStorage("jHueTourExtras"), function (cnt, tour) {
+                var _found = false;
+                $.each(json.tours, function (icnt, itour) {
+                  if (itour.name == tour.name) {
+                    _found = true;
+                  }
+                });
+                if (!_found) {
+                  _newStoredArray.push(tour);
+                }
+              });
+            }
+            $.totalStorage("jHueTourExtras", _newStoredArray);
+          }
+          $("#jHueTourQuestion").popover("destroy");
+          _this.initQuestionMark();
+          $("#jHueTourQuestion").click();
+        },
+        error: function (e) {
+          if ($.jHueNotify !== undefined) {
+            $.jHueNotify.error(e.message)
+          }
+          $("#jHueTourRemoteTutorial").removeAttr("disabled");
+          $("#jHueTourRemoteTutorialBtn").removeAttr("disabled");
+        }
+      });
+    }
+
+    if (_op.indexOf("remove_") == 0) {
+      var _tourName = _op.substr(7);
+      if ($.totalStorage !== undefined) {
+        var _newStoredArray = [];
+        $.each($.totalStorage("jHueTourExtras"), function (cnt, tour) {
+          if (tour.name != _tourName) {
+            _newStoredArray.push(tour);
+          }
+        });
+        $.totalStorage("jHueTourExtras", _newStoredArray);
+        $("#jHueTourQuestion").popover("destroy");
+        _this.initQuestionMark();
+        $("#jHueTourQuestion").click();
+      }
+    }
+
+    if (_op == "start" || _op == "show" || _op == "play") {
+      if (_this.options.tours.length > 0 && _this.currentTour.name == "") {
+        _this.currentTour.name = _this.options.tours[0].name;
+        _this.currentTour.path = _this.options.tours[0].path;
+        _this.currentTour.steps = _this.options.tours[0].steps;
+      }
+      this.showStep(1);
+    }
+
+    if (_op == "reset") {
+      if ($.totalStorage !== undefined) {
+        $.totalStorage("jHueTourHistory", null);
+        $.totalStorage("jHueTourExtras", null);
+      }
+    }
+
+    if (_op == "clear") {
+      _this.options.tours = [];
+    }
+
+    if (_op == "end" || _op == "hide" || _op == "close" || _op == "stop") {
+      _this.closeCurtains();
+    }
+  };
+
+  Plugin.prototype.closeCurtains = function () {
+    $(".popover").remove();
+    $(".jHueTourExposed").removeClass("jHueTourExposed");
+    $("#jHueTourMask").hide();
+  };
+
+  Plugin.prototype.showTour = function (tourName, stepNo) {
+    var _this = this;
+    if (_this.options.tours != null) {
+      $.each(_this.options.tours, function (cnt, tour) {
+        if (tour.name == tourName && (tour.path === undefined || RegExp(tour.path).test(location.pathname))) {
+          _this.currentTour.name = tour.name;
+          _this.currentTour.path = tour.path;
+          _this.currentTour.steps = tour.steps;
+          if (stepNo === undefined) {
+            _this.showStep(1);
+          }
+          else {
+            _this.showStep(stepNo);
+          }
+          return;
+        }
+      });
+    }
+  };
+
+  Plugin.prototype.showStep = function (stepNo) {
+    var _this = this;
+    if (_this.currentTour.steps[stepNo - 1] != null) {
+      var _step = _this.currentTour.steps[stepNo - 1];
+      _this.currentTour.shownStep = stepNo;
+      var _navigation = "";
+
+      if (_step.visitUrl != undefined) {
+        location.href = _step.visitUrl;
+      }
+
+      if (_step.onShown != undefined) {
+        window.setTimeout(_step.onShown, 10);
+      }
+
+      $(".popover").remove();
+      $(".jHueTourExposed").removeClass("jHueTourExposed");
+      if ($(".jHueTourExposed").css("position") == "relative") {
+        $(".jHueTourExposed").css("position", "relative");
+      }
+      $("#jHueTourMask").width($(document).width()).height($(document).height()).show();
+
+      var _closeBtn = $("<a>");
+      _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
+      _closeBtn.click(function () {
+        _this.performOperation("close");
+      });
+
+      var _nextBtn = $("<a>");
+      _nextBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-chevron-sign-right"></i>').css("margin-top", "10px");
+      _nextBtn.click(function () {
+        _this.showStep(_this.currentTour.shownStep + 1);
+      });
+
+      var _prevBtn = $("<a>");
+      _prevBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-chevron-sign-left"></i>').css("margin-top", "10px").css("margin-right", "10px");
+      _prevBtn.click(function () {
+        _this.showStep(_this.currentTour.shownStep - 1);
+      });
+
+      var _arrowOn = _step.arrowOn;
+      var _additionalContent = "";
+      if ($(_arrowOn).length == 0 || !($(_arrowOn).is(":visible"))){
+        _arrowOn = "body";
+        _additionalContent = "<b>MISSING POINTER OF STEP "+ _this.currentTour.shownStep +"</b> ";
+      }
+      $(_arrowOn).popover('destroy').popover({
+        title: _step.title,
+        content: _additionalContent + _step.content + "<br/>",
+        html: true,
+        trigger: 'manual',
+        placement: (_step.placement != "" && _step.placement != undefined) ? _step.placement : "left"
+      }).popover('show');
+
+      if (_step.top != undefined) {
+        if ($.isNumeric(_step.top)) {
+          $(".popover").css("top", ($(".popover").position().top + _step.top) + "px");
+        }
+        else {
+          $(".popover").css("top", _step.top);
+        }
+      }
+
+      if (_step.left != undefined) {
+        if ($.isNumeric(_step.left)) {
+          $(".popover").css("left", ($(".popover").position().left + _step.left) + "px");
+        }
+        else {
+          $(".popover").css("left", _step.left);
+        }
+      }
+      $(".popover-title").html(_step.title);
+      _closeBtn.prependTo($(".popover-title"));
+
+      if (_this.currentTour.shownStep > 1) {
+        _prevBtn.appendTo($(".popover-content p"));
+      }
+      if (_this.currentTour.shownStep < _this.currentTour.steps.length && (_step.waitForAction == undefined || _step.waitForAction == false)) {
+        _nextBtn.appendTo($(".popover-content p"));
+      }
+
+      // last step, mark tour/tutorial as done
+      if ($.totalStorage !== undefined && _this.currentTour.shownStep == _this.currentTour.steps.length) {
+        var _key = location.pathname;
+        if (_this.currentTour.path !== undefined && _this.currentTour.path != "") {
+          _key = _this.currentTour.path;
+        }
+        _key += "_" + _this.currentTour.name;
+        var _history = $.totalStorage("jHueTourHistory");
+        if (_history == null) {
+          _history = {}
+        }
+        _history[_key] = true;
+        $.totalStorage("jHueTourHistory", _history);
+      }
+
+      var _exposedElement = $((_step.expose != undefined && _step.expose != "" ? _step.expose : _arrowOn));
+      if (_exposedElement.css("position") === undefined || _exposedElement.css("position") != "fixed") {
+        _exposedElement.css("position", "relative");
+      }
+      _exposedElement.addClass("jHueTourExposed");
+    }
+  };
+
+  $[pluginName] = function (options, stepNo) {
+    var _el = $("body");
+    if (!$("body").data('plugin_' + pluginName)) {
+      $("body").data('plugin_' + pluginName, new Plugin(_el, options));
+    }
+
+    if (options === undefined) {
+      return $("body").data('plugin_' + pluginName).availableTours();
+    }
+
+    if (typeof options == "string") {
+      if (stepNo === undefined) {
+        $("body").data('plugin_' + pluginName).performOperation(options);
+      }
+      else if ($.isNumeric(stepNo)) {
+        $("body").data('plugin_' + pluginName).showTour(options, stepNo);
+      }
+    }
+    else if ($.isNumeric(options)) {
+      $("body").data('plugin_' + pluginName).showStep(options);
+    }
+    else {
+      $("body").data('plugin_' + pluginName).addTours(options);
+    }
+  };
+
+})(jQuery, window, document);

+ 24 - 0
tools/followme/jHueTourBuilder.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+
+	<title>jHueTour Builder</title>
+	
+</head>
+
+<body>
+	
+	
+
+Drag the bookmarklet to your fav bar: 
+
+<a href="javascript:(function()%7Bfunction%20callback()%7Bif%20(typeof%20console%20!%3D%20%22undefined%22)%20%7B%20console.log(%22Welcome%20to%20jHueTourBuilder!%22)%3B%7Dif%20(typeof%20%24%20%3D%3D%20%22undefined%22%20%7C%7C%20typeof%20%24.jHueTour%20%3D%3D%20%22undefined%22)%20%7B%20alert(%22Sorry%2C%20but%20you%20need%20to%20run%20this%20while%20being%20on%20Hue!%22)%3B%20%7D%7Dvar%20s%3Ddocument.createElement(%22script%22)%3Bs.src%3D%22https%3A%2F%2Fdl.dropboxusercontent.com%2Fu%2F730827%2Fcloudera%2FjHueTourBuilder.js%22%3Bif(s.addEventListener)%7Bs.addEventListener(%22load%22%2Ccallback%2Cfalse)%7Delse%20if(s.readyState)%7Bs.onreadystatechange%3Dcallback%7Ddocument.body.appendChild(s)%3B%7D)()">jHueTour Builder</a>
+
+<br/><br/>
+<em>Thanks to Peter Coles for the amazing <a href="http://mrcoles.com/bookmarklet/">Bookmarklet generator</a></em>
+
+</body>
+</html>

+ 475 - 0
tools/followme/jHueTourBuilder.js

@@ -0,0 +1,475 @@
+$("<style type='text/css'> .tooltip {z-index: 30000} .tourBuilder td { vertical-align: text-top } .tourBuilder span[rel='tooltip'] {cursor: pointer; color: #FFF; font-size: 14px} .tourBuilder { overflow-y: auto; overflow-x: hidden; color: #FFF; font-size: 11px; background-color: #333; padding: 10px; opacity: 0.9; position: fixed; width: 360px; right: 0; top: 0; z-index: 15000 } .tourBuilder select, .tourBuilder textarea  { font-size: 11px; } .tourBuilder input { font-size: 11px; height: 14px } </style>").appendTo($("head"));
+
+     var _tbTour = {
+       name: "",
+       desc: "",
+       path: location.pathname,
+       steps: []
+     };
+
+     var _origTbTour = _tbTour;
+
+     var _tb = $("<div>");
+     _tb.addClass("tourBuilder");
+     _tb.height($(window).height());
+     _tb.appendTo($("body"));
+
+     var _tbCog = $("<div>").attr("id", "tbCog").html('<i class="icon-cogs"></i>').addClass("jHueTourBadge").css("top", "64px");
+     _tbCog.on("click", function () {
+       _tb.show();
+     });
+
+     _tbCog.appendTo($("body"));
+
+     var _html = "<h4>General</h4>" +
+             "<table><tr><td>ID:</td><td><input class='input' type='text' id='tbName' /> <span rel='tooltip' title='unique tour name (location.pathname scope)'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Name:</td><td><input class='input' type='text' id='tbDesc' /> <span rel='tooltip' title='the label shown on the question mark'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Path:</td><td><input class='input' type='text' id='tbPath' value='" + _tbTour.path + "'/> <span rel='tooltip' title='string for the path to show this tour on, regex allowed'><i class='icon-question-sign'></i></span></td></tr></table>" +
+             "<h4>New step</h4><table>" +
+             "<tr><td>Visit URL:</td><td><input class='input' type='text' id='tbNewURL' /> <span rel='tooltip' title='overrides everything, redirects to specific url'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td colspan='2'>&nbsp;</td></tr>" +
+             "<tr><td>Arrow on:</td><td><input class='input' type='text' id='tbNewArrow' /> <span rel='tooltip' title='the element relative to the popover is positioned'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Expose:</td><td><input class='input' type='text' id='tbNewExpose' /> <span rel='tooltip' title='optional, the exposed object. if not present, arrowOn will be exposed'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Title:</td><td><input class='input' type='text' id='tbNewTitle'/> <span rel='tooltip' title='popover title'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Content:</td><td><textarea id='tbNewContent'></textarea> <span rel='tooltip' title='popover content, html enabled'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Placement:</td><td><select class='input' id='tbNewPlacement'><option value='top' selected='selected'>Top</option><option value='bottom'>Bottom</option><option value='left'>Left</option><option value='right'>Right</option> </select> <span rel='tooltip' title='popover placement'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Left:</td><td>Offset <input type='text' class='input-mini' id='tbNewOffsetLeft'/> Abs <input type='text' class='input-mini' id='tbNewAbsLeft'/></td></tr>" +
+             "<tr><td>Top:</td><td>Offset <input type='text' class='input-mini' id='tbNewOffsetTop'/> Abs <input type='text' class='input-mini' id='tbNewAbsTop'/></td></tr>" +
+             "<tr><td>On shown:</td><td><textarea id='tbNewShown'></textarea> <span rel='tooltip' title='javascript run on popover step shown. DANGER!'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>Wait:</td><td><input type='checkbox' id='tbNewWait'/> <span rel='tooltip' title='does not show the next button and waits for an action to go on with the tour'><i class='icon-question-sign'></i></span></td></tr>" +
+             "<tr><td>&nbsp;</td><td style='text-align: right'><a href='javascript:void(0)' id='tbNewShow' class='btn btn-mini'>Show</a> <a id='tbNewAdd' href='javascript:void(0)' class='btn btn-mini'>Add to tour</a> </td></tr>" +
+             "</table><h4>Steps</h4>" +
+             "<div id='tbSteps'></div>" +
+             "<h4>JSON</h4>" +
+             "<textarea id='tbPreview' style='width: 90%'>" + JSON.stringify(_tbTour) + "</textarea><br/><a href='javascript:void(0)' id='tbLoadJson' class='btn btn-mini'>Load from JSON</a><br/><br/>" +
+             "<div style='text-align: right'><a id='tbManageTours' class='btn' href='javascript:void(0)' style='float:left'><i class='icon-sitemap'></i></a> <a id='tbAddTour' class='btn' href='javascript:void(0)'><i class='icon-plus-sign-alt'></i></a> <a id='tbClearCache' class='btn' href='javascript:void(0)'><i class='icon-trash'></i></a> <a id='tbTryTour' title='Try tour' class='btn' href='javascript:void(0)'><i class='icon-eye-open'></i></a></div><br/><br/>";
+
+
+     _tb.html(_html);
+
+
+     var _managerHtml = "<div id='tbToursModal' class='modal hide fade'>" +
+             "<div class='modal-header'>" +
+             "<button type='button' class='close' data-dismiss='modal' aria-hidden='true'>&times;</button>" +
+             "<h3>Available tours</h3>" +
+             "</div>" +
+             "<div class='modal-body'>" +
+             "<p><span id='tbTourList'></span><br/><h4>Content for available.tours.js</h4><textarea id='tbAvailableContent' style='width:90%'></textarea><br/><a href='javascript:void(0)' id='tbLoadToursJson' class='btn btn-mini'>Load from JSON</a></p>" +
+             "</div>" +
+             "<div class='modal-footer'><a href='javascript:void(0)' data-dismiss='modal' class='btn'>Close</a></div></div>";
+
+     $(_managerHtml).appendTo($("body"));
+
+
+     $("span[rel='tooltip']").tooltip({
+       placement: "left"
+     });
+
+     _tb.find("input").on("change", function () {
+       updateTour();
+     });
+
+     $("textarea").on("focus", function () {
+       var $this = $(this);
+       $this.select();
+
+       // Work around Chrome's little problem
+       $this.mouseup(function () {
+         // Prevent further mouseup intervention
+         $this.unbind("mouseup");
+         return false;
+       });
+     });
+
+     var _tbTourIsEditing = -1;
+     var _tbTourIsStepEditing = -1;
+
+
+     function updateTour() {
+       _tbTour.name = $("#tbName").val();
+       _tbTour.desc = $("#tbDesc").val();
+       _tbTour.path = $("#tbPath").val();
+
+       if ($("#tbNewExpose").val() == "") {
+         $("#tbNewExpose").val($("#tbNewArrow").val());
+       }
+       function fillItem(item) {
+         $("#tbNewURL").val(item.visitUrl);
+         $("#tbNewArrow").val(item.arrowOn);
+         $("#tbNewExpose").val(item.expose);
+         $("#tbNewTitle").val(item.title);
+         $("#tbNewContent").val(item.content);
+         $("#tbNewPlacement").val(item.placement);
+         $("#tbNewShown").val(item.onShown);
+         if (item.waitForAction == true){
+           $("#tbNewWait").prop("checked", true);
+         }
+         else {
+           $("#tbNewWait").prop("checked", false);
+         }
+         $("#tbNewOffsetTop").val("");
+         $("#tbNewAbsTop").val("");
+         $("#tbNewOffsetLeft").val("");
+         $("#tbNewAbsLeft").val("");
+         if (item.left != undefined && item.left != "") {
+           if ($.isNumeric(item.left)) {
+             $("#tbNewOffsetLeft").val(item.left);
+           }
+           else {
+             $("#tbNewAbsLeft").val(item.left.substring(0, item.left.length - 2));
+           }
+         }
+         if (item.top != undefined && item.top != "") {
+           if ($.isNumeric(item.top)) {
+             $("#tbNewOffsetTop").val(item.top);
+           }
+           else {
+             $("#tbNewAbsTop").val(item.top.substring(0, item.top.length - 2));
+           }
+         }
+       }
+
+       $("#tbSteps").empty();
+       $(_tbTour.steps).each(function (cnt, item) {
+         var _trow = $("<div>").html("&nbsp;" + (item.visitUrl!="" && item.visitUrl!=undefined?"URL: " + item.visitUrl:"Title: "+item.title));
+
+         var _trowdelete = $("<a>");
+         _trowdelete.addClass("btn").addClass("btn-mini").html("<i class='icon-trash'></i>").css("margin", "3px").on("click", function () {
+           _tbTour.steps.splice(cnt, 1);
+           updateTour();
+         });
+         _trowdelete.prependTo(_trow);
+
+         var _trowcopy = $("<a>");
+         _trowcopy.addClass("btn").addClass("btn-mini").html("<i class='icon-copy'></i>").css("margin", "3px").on("click", function () {
+           fillItem(item);
+         });
+         _trowcopy.prependTo(_trow);
+
+         var _trowedit = $("<a>");
+         _trowedit.addClass("btn").addClass("btn-mini").html("<i class='icon-edit'></i>").css("margin", "3px").on("click", function () {
+           fillItem(item);
+           _tbTourIsStepEditing = cnt;
+           $("#tbNewAdd").text("Update step");
+         });
+         _trowedit.prependTo(_trow);
+
+         var _trowup = $("<a>");
+         _trowup.addClass("btn").addClass("btn-mini").html("<i class='icon-arrow-up'></i>").css("margin", "3px").on("click", function () {
+           var _newPos = cnt - 1;
+           if (cnt == 0) {
+             _newPos = _tbTour.steps.length - 1;
+           }
+           _tbTour.steps.move(cnt, _newPos);
+           updateTour();
+         });
+         _trowup.prependTo(_trow);
+
+         var _trowdown = $("<a>");
+         _trowdown.addClass("btn").addClass("btn-mini").html("<i class='icon-arrow-down'></i>").css("margin", "3px").on("click", function () {
+           var _newPos = cnt + 1;
+           if (cnt == _tbTour.steps.length - 1) {
+             _newPos = 0;
+           }
+           _tbTour.steps.move(cnt, _newPos);
+           updateTour();
+         });
+         _trowdown.prependTo(_trow);
+
+         _trow.appendTo($("#tbSteps"));
+       });
+
+       $("#tbPreview").val(JSON.stringify(_tbTour));
+
+       $.totalStorage("jHueTourBuilderTemp", _tbTour);
+     }
+
+     $("#tbLoadJson").on("click", function () {
+       _tbTourIsStepEditing = -1;
+       try {
+         _tbTour = JSON.parse($.trim($("#tbPreview").val()));
+         $("#tbName").val(_tbTour.name);
+         $("#tbDesc").val(_tbTour.desc);
+         $("#tbPath").val(_tbTour.path);
+         updateTour();
+       }
+       catch (exception) {
+         alert("It seems you don't eat JSON for breakfast:" + exception)
+       }
+     });
+
+      $("#tbLoadToursJson").on("click", function () {
+        _tbTourIsEditing = -1;
+       _tbTourIsStepEditing = -1;
+       try {
+         var _paste = $.trim($("#tbAvailableContent").val());
+         if (_paste.indexOf("$.jHueTour") > -1){
+           _paste = _paste.substr(11);
+         }
+         if (_paste[_paste.length - 1] == ";"){
+           _paste = _paste.substring(0, _paste.length - 2);
+         }
+         var _trz = JSON.parse(_paste);
+         $.totalStorage("jHueTourBuilderTours", _trz.tours);
+         $("#tbManageTours").click();
+       }
+       catch (exception) {
+         alert("It seems you don't eat JSON for breakfast:" + exception)
+       }
+     });
+
+     $("#tbNewShow").on("click", function () {
+       $(".popover").remove();
+       _tbTourIsEditing = -1;
+       _tbTourIsStepEditing = -1;
+       $(".jHueTourExposed").removeClass("jHueTourExposed").css("position", "");
+       $("#jHueTourMask").width($(document).width()).height($(document).height()).show();
+       var _closeBtn = $("<a>");
+       _closeBtn.addClass("btn").addClass("btn-mini").html('<i class="icon-remove"></i>').css("float", "right").css("margin-top", "-4px").css("margin-right", "-6px");
+       _closeBtn.click(function () {
+         $(".popover").remove();
+         $(".jHueTourExposed").removeClass("jHueTourExposed");
+         $("#jHueTourMask").hide();
+       });
+
+       if ($($("#tbNewArrow").val()).length > 0) {
+         $($("#tbNewArrow").val()).popover('destroy').popover({
+           title: $("#tbNewTitle").val(),
+           content: $("#tbNewContent").val() + "<br/>",
+           html: true,
+           trigger: 'manual',
+           placement: $("#tbNewPlacement").val()
+         }).popover('show');
+         if ($("#tbNewOffsetTop").val() != "") {
+           $(".popover").css("top", ($(".popover").position().top + ($("#tbNewOffsetTop").val() * 1)) + "px");
+         }
+         if ($("#tbNewAbsTop").val() != "") {
+           $(".popover").css("top", $("#tbNewAbsTop").val() + "px");
+         }
+         if ($("#tbNewOffsetLeft").val() != "") {
+           $(".popover").css("left", ($(".popover").position().left + ($("#tbNewOffsetLeft").val() * 1)) + "px");
+         }
+         if ($("#tbNewAbsLeft").val() != "") {
+           $(".popover").css("left", $("#tbNewAbsLeft").val() + "px");
+         }
+         $(".popover-title").html($("#tbNewTitle").val());
+         _closeBtn.prependTo($(".popover-title"));
+         $("<a class='btn btn-mini' style='margin-top:10px' onclick='alert(\"Nothing else to do? :)\")'><i class='icon-smile'></i></a>").appendTo($(".popover-content p"));
+       }
+
+       var _exposedElement = $($("#tbNewExpose").val());
+       if (_exposedElement.css("position") === undefined || _exposedElement.css("position") != "fixed") {
+         _exposedElement.css("position", "relative");
+       }
+       _exposedElement.addClass("jHueTourExposed");
+
+       if ($("#tbNewShown").val() != "") {
+         window.setTimeout($("#tbNewShown").val(), 10);
+       }
+     });
+
+     Array.prototype.move = function (old_index, new_index) {
+       if (new_index >= this.length) {
+         var k = new_index - this.length;
+         while ((k--) + 1) {
+           this.push(undefined);
+         }
+       }
+       this.splice(new_index, 0, this.splice(old_index, 1)[0]);
+       return this;
+     };
+
+     $("#tbNewAdd").on("click", function () {
+       var _newStep = {
+         arrowOn: $("#tbNewArrow").val(),
+         expose: $("#tbNewExpose").val(),
+         title: $("#tbNewTitle").val(),
+         content: $("#tbNewContent").val(),
+         placement: $("#tbNewPlacement").val(),
+         onShown: $("#tbNewShown").val(),
+         waitForAction: $("#tbNewWait").is(":checked")
+       };
+
+       if ($("#tbNewURL").val() != ""){
+         _newStep = {
+           visitUrl: $("#tbNewURL").val()
+         }
+       }
+
+       if ($("#tbNewOffsetTop").val() != "") {
+         _newStep.top = $("#tbNewOffsetTop").val() * 1;
+       }
+       if ($("#tbNewAbsTop").val() != "") {
+         _newStep.top = $("#tbNewAbsTop").val() + "px";
+       }
+       if ($("#tbNewOffsetLeft").val() != "") {
+         _newStep.left = $("#tbNewOffsetLeft").val() * 1;
+       }
+       if ($("#tbNewAbsLeft").val() != "") {
+         _newStep.left = $("#tbNewAbsLeft").val() + "px";
+       }
+       if (_tbTourIsStepEditing > -1) {
+         _tbTour.steps[_tbTourIsStepEditing] = _newStep;
+         _tbTourIsStepEditing = -1;
+         $("#tbNewAdd").text("Add to tour");
+       }
+       else {
+         _tbTour.steps.push(_newStep);
+       }
+       updateTour();
+       $(".popover").remove();
+       $(".jHueTourExposed").removeClass("jHueTourExposed");
+       $("#jHueTourMask").hide();
+
+       $("#tbNewURL").val("");
+       $("#tbNewArrow").val("");
+       $("#tbNewExpose").val("");
+       $("#tbNewTitle").val("");
+       $("#tbNewContent").val("");
+       $("#tbNewShown").val("");
+       $("#tbNewWait").prop("checked", false);
+       $("#tbNewPlacement").val("");
+       $("#tbNewOffsetTop").val("");
+       $("#tbNewAbsTop").val("");
+       $("#tbNewOffsetLeft").val("");
+       $("#tbNewAbsLeft").val("");
+     });
+
+     $("#tbTryTour").on("click", function () {
+       $.jHueTour("clear");
+       $.jHueTour({ tours: [_tbTour]});
+       _tb.hide();
+       $.jHueTour("play");
+     });
+
+     $("#tbClearCache").on("click", function () {
+       $.totalStorage("jHueTourBuilderTemp", null);
+       _tbTour = _origTbTour;
+       $("#tbName").val(_tbTour.name);
+       $("#tbDesc").val(_tbTour.desc);
+       $("#tbPath").val(_tbTour.path);
+       updateTour();
+     });
+
+     $("#tbAddTour").on("click", function () {
+
+       var _tbStoredTours = [];
+       if ($.totalStorage("jHueTourBuilderTours") != null){
+         _tbStoredTours = $.totalStorage("jHueTourBuilderTours");
+       }
+
+       if (_tbTourIsEditing > -1) {
+         _tbStoredTours[_tbTourIsEditing] = _tbTour;
+         _tbTourIsEditing = -1;
+         $("#tbAddTour").html("<i class='icon-plus-sign-alt'></i>");
+       }
+       else {
+         _tbStoredTours.push(_tbTour);
+       }
+
+       $.totalStorage("jHueTourBuilderTours", _tbStoredTours);
+       $("#tbClearCache").click();
+     });
+
+     $("#tbManageTours").on("click", function () {
+       $("#tbTourList").empty();
+       $($.totalStorage("jHueTourBuilderTours")).each(function (cnt, item) {
+         var _trow = $("<div>").html("&nbsp;Name: " + item.name + ", Desc: "+ item.desc + ", Path: "+ item.path);
+
+         var _trowdelete = $("<a>");
+         _trowdelete.addClass("btn").addClass("btn-mini").html("<i class='icon-trash'></i>").css("margin", "3px").on("click", function () {
+           var _tmpTrz = $.totalStorage("jHueTourBuilderTours");
+           _tmpTrz.splice(cnt, 1);
+           $.totalStorage("jHueTourBuilderTours", _tmpTrz);
+           $("#tbManageTours").click();
+         });
+         _trowdelete.prependTo(_trow);
+
+         var _trowcopy = $("<a>");
+         _trowcopy.addClass("btn").addClass("btn-mini").html("<i class='icon-copy'></i>").css("margin", "3px").on("click", function () {
+           $.totalStorage("jHueTourBuilderTemp", null);
+           _tbTour = item;
+           $("#tbName").val(_tbTour.name);
+           $("#tbDesc").val(_tbTour.desc);
+           $("#tbPath").val(_tbTour.path);
+           updateTour();
+           $("#tbToursModal").modal("hide");
+         });
+         _trowcopy.prependTo(_trow);
+
+         var _trowedit = $("<a>");
+         _trowedit.addClass("btn").addClass("btn-mini").html("<i class='icon-edit'></i>").css("margin", "3px").on("click", function () {
+           $.totalStorage("jHueTourBuilderTemp", null);
+           _tbTour = item;
+           $("#tbName").val(_tbTour.name);
+           $("#tbDesc").val(_tbTour.desc);
+           $("#tbPath").val(_tbTour.path);
+           updateTour();
+           _tbTourIsEditing = cnt;
+           $("#tbAddTour").html("<i class='icon-save'></i>");
+           $("#tbToursModal").modal("hide");
+         });
+         _trowedit.prependTo(_trow);
+
+         _trow.appendTo($("#tbTourList"));
+       });
+
+       var _trz = {
+         tours: $.totalStorage("jHueTourBuilderTours")
+       };
+       $("#tbAvailableContent").val("$.jHueTour(" + JSON.stringify(_trz) + ");");
+       $("#tbToursModal").modal("show");
+     });
+
+     var _changeSide = $("<a>");
+     _changeSide.addClass("btn").addClass("btn-mini").addClass("pull-right");
+     _changeSide.on("click", function (e) {
+       e.preventDefault();
+       e.stopImmediatePropagation();
+       if (_tb.css("left") != "0px") {
+         _tb.css("left", "0");
+         _tb.css("right", "auto");
+         $(this).html("<i class='icon-arrow-right'></i>");
+       }
+       else {
+         _tb.css("right", "0");
+         _tb.css("left", "auto");
+         $(this).html("<i class='icon-arrow-left'></i>");
+       }
+     });
+     _changeSide.html("<i class='icon-arrow-left'></i>");
+     _changeSide.prependTo(_tb);
+
+     var _hideTb = $("<a>");
+     _hideTb.addClass("btn").addClass("btn-mini").addClass("pull-right").css("margin-left", "4px");
+     _hideTb.on("click", function (e) {
+       e.preventDefault();
+       e.stopImmediatePropagation();
+       _tb.hide();
+     });
+     _hideTb.html("<i class='icon-remove'></i>");
+     _hideTb.prependTo(_tb);
+
+      // preload
+      if ($.totalStorage("jHueTourBuilderTemp") != null){
+       _tbTour = $.totalStorage("jHueTourBuilderTemp");
+       $("#tbName").val(_tbTour.name);
+       $("#tbDesc").val(_tbTour.desc);
+       $("#tbPath").val(_tbTour.path);
+       updateTour();
+     }
+     if ($.totalStorage("jHueTourBuilderTours") == null){
+       $.totalStorage("jHueTourBuilderTours", []);
+     }
+      else {
+       $($.totalStorage("jHueTourBuilderTours")).each(function (cnt, item) {
+         if (item.name == _tbTour.name && item.path == _tbTour.path){
+            _tbTourIsEditing = cnt;
+           $("#tbAddTour").html("<i class='icon-save'></i>");
+         }
+       });
+     }
+
+     $(window).resize(function () {
+       _tb.height($(window).height());
+     });