2009-07-31 145 views
1

我有以下插件,它采用部分游戏名称,将其从我们的DataQuery对象弹出以获取服务器中的项目列表(基本自动补全/选择器)。Javascript命名空间冲突

我遇到的问题是这样的。

我在页面上使用它,其中选择器出现在对话框中。当用户完成时,我'销毁'选择器,然后在他们再次需要时重新创建它。这是因为在这个页面上,用户有时候会添加一个新的游戏或者编辑,所以我必须改变它。

添加能力设置resultsChange选项true,因为如果他们希望用户可以更改游戏选择。

编辑能力设置resultsChange选项false,因为它是锁着的。

然而,选择的所有后续用途只能访问其通过首次选择对象。这就像使用destroy方法实际上不会删除以前的选项对象。

我看不到我如何解决这个问题。任何帮助,将不胜感激。

(function($){ 

    $.fn.Napalm_GameSelector = function(settings) { 
     if (this.length > 1) { return false; } 
     var $element = $(this); 

     if (settings == 'destroy') { 
      if (!$element.data('Napalm_Selector')) { return; } 
      $element.data('Napalm_Selector').destroy(); 
      $element.removeData('Napalm_Selector'); 
      return; 
     } 
     if ($element.data('Napalm_Selector')) { return; } 

     /* Verify parent element has id */ 
     if ($element.attr('id').length < 1) { 
      Napalm_Error.failure('Base element has no ID'); 
      return false; 
     } 

     /* Verify parent element type */ 
     if ($element.attr('type') !== 'text') { 
      Napalm_Error.failure('Must be attached to a text field'); 
      return false; 
     } 

     $element.data('Napalm_Selector', new SelectorObject(this, settings)); 
     delete settings; 
     delete $element; 
    }; 

    var defaults = { 
      /* General */ 
     id: false, 
     formname: false, 
     selectedId: false, 
     callTyping: false, 
     callStart: false, 
     callComplete: false, 
     callResults: false, 
     callNoresults: false, 
     callSelected: false, 
     callUnselected: false, 
     classLoader: 'dataselector_loader', 
     classResults: 'dataselector_results', 
     classNoresults: 'dataselector_noresults', 
     classTruncated: 'dataselector_truncated', 
     keyDelay: 1500, 
     keyLength: 2, 
     resultsTimeout: 0, 
     resultsOffclick: true, 
     resultsAnchor: 'left', 
     resultsChange: true, 
     resultsChangeText: 'Change Game', 
      /* Specific */ 
     showGamebox: false, 
     showGameinfo: true, 
     classBoxart: 'boxart', 
     infoBackground: false 
    }; 

    var SelectorObject = function(element, settings) { 

     var $element = $(element); 
     var obj = this; 

     var options = $.extend({}, defaults, settings); 

     var componentName = 'User_My_GamesLibrary'; 

     var id = false; 
     var menuTimeout = false; 
     var keyTimeout = false; 

     var typingStarted = false; 
     var typingFinished = false; 


     /* INIT */ 

     /* Option: ID */ 
     if (options.id !== false) { 
      id = options.id; 
      $element.attr('id', id); 
     } else { 
      id = $element.attr('id'); 
     } 

     $element.parent().attr('onSubmit','javascript:return false;'); 
     $element.attr('autocomplete','off'); 


     /* METHODS */ 

     this.select = function(element) { 
      var self = this; 
      $element.val(''); 
      if (!parseInt(element)) { 
       itemid = $(element).attr('rel'); 
      } else { 
       itemid = element; 
      } 
      $element.hide(); 
      $('#'+id+'-formelement').attr('value', itemid); 
      $element.after(this.templates.selected(id+'-selected', itemid)); 
      /* Change Link */ 
      if (options.resultsChange) { 
       $('#'+id+'-selected a').click(function() { 
        /* User Callback: callUnselected */ 
        if (typeof(options.callUnselected) == 'function') { options.callUnselected(); } 

        self.reset(); 
        return false; 
       }); 
      } 
      /* Clean up */ 
      this.clear(); 
      /* User Callback: callComplete */ 
      if (typeof(options.callSelected) == 'function') { options.callSelected(itemid); } 
     } 

     this.binding = function() { 
      var self = this; 
      $element.bind('keydown click', function(e) { 
       clearTimeout(keyTimeout); 
       if (e.keyCode == 13 || e.type == 'click') { 
        if ($.trim($element.val()).length >= options.keyLength) { 
         self.search(); 
        } 
       } else if (e.keyCode != 38 && e.keyCode != 40) { 
        /* User Callback: callStart */ 
        if (!typingStarted) { 
         typingStarted = true; 
         if (typeof(options.callStart) == 'function') { options.callStart(); } 
        } 
        /* User Callback: callTyping */ 
        if (typeof(options.callTyping) == 'function') { options.callTyping(); } 

        if ($.trim($element.val()).length >= options.keyLength) { 
         keyTimeout = setTimeout(function() { 
          self.search(); 
         },options.keyDelay); 
        } 
       } 
      }); 
     } 

     this.search = function() { 
      var self = this; 
      /* User Callback: callEnd */ 
      if (typeof(options.callEnd) == 'function') { options.callEnd(); } 
      /* Remove Any Existing Elements */ 
      this.clear(); 
      /* Content Exists? */ 
      if ($element.val().length < 1) { return false; } 
      /* Loading Template */ 
      $element.after(this.templates.loading(id+'-loading', options.classLoader)); 
      $('#'+id+'-loading').css('position','absolute'); 
      $('#'+id+'-loading').css({ 
       top: ($element.position().top+$element.outerHeight(true))+'px', 
       left: $element.position().left+'px' 
      }); 
      /* Get Data */ 
      Napalm_DataQuery['getGames']($.trim($element.val()), function(data) { 
       /* Remove Loading Template */ 
       $('#'+id+'-loading').remove(); 
       /* Setup Offclick */ 
       if (options.resultsOffclick) { 
        $('body').bind('click',function() { self.clear(); }); 
       } 
       if (data['count']) { 
        /* Build Item Data */ 
        var items = ''; 
        $.each(data['items'], function(k, v) { 
         items += self.templates.resultsitem(id+'-item-'+v['id'], v['id'], v['title']); 
        }); 
        if (data['truncated']) { 
         items += self.templates.truncateditem(id+'-item-truncated', options.classTruncated); 
        } 
        /* Inject Results */ 
        $('body').append(self.templates.results(id+'-results', items, options.classResults)); 
        //$element.after(self.templates.results(id+'-results', items, options.classResults)); 
        $results = $('#'+id+'-results'); 
        var offset = $element.offset(); 
        $results 
         .css({ 
          zIndex: 9999, 
          position: 'absolute', 
          minWidth: $element.outerWidth(), 
          top: Math.round(offset.top+$element.innerHeight())+'px', 
          left: Math.round(offset.left)+'px' 
         }); 

        /* 
        switch (options.resultsAnchor.toLowerCase()) { 
         case 'right': 
          $results.css('left',(Napalm_Position.absolute($element).right-Napalm_Position.width('#'+id+'-results'))+'px'); 
          break; 
         case 'left': 
          $results.css('left',Napalm_Position.absolute($element).left+'px'); 
          break; 
        } 
        */ 

        $resultsItems = $('ul > li:not(.truncated)', $results); 

        /* Binding Clicks */ 
        $resultsItems.click(function() { 
         self.select(this); 
        }); 
        /* Handle Arrow Keys */ 
        var resultIndex = -1; 
        $(window).keydown(function(e) { 
         switch (e.keyCode) { 
          case 38: /* Up Arrow */ 
           $element.blur() 
           if (resultIndex > 0) { 
            resultIndex--; 
            /* Release Previous */ 
            if (resultIndex < $resultsItems.size()-1) { 
             node = $resultsItems[resultIndex+1]; 
             $(node).removeClass('active'); 
             delete node; 
            } 
            node = $resultsItems[resultIndex]; 
            $(node).addClass('active'); 
            /* Container Scrolling */ 
            self.scroll(node); 
            delete node; 
           } 
           return false; 
           break; 
          case 40: /* Down Arrow */ 
           $element.blur() 
           if (resultIndex < $resultsItems.size()-1) { 
            resultIndex++; 
            /* Release Previous */ 
            if (resultIndex > 0) { 
             node = $resultsItems[resultIndex-1]; 
             $(node).removeClass('active'); 
             delete node; 
            } 
            /* Paint New */ 
            node = $resultsItems[resultIndex]; 
            $(node).addClass('active'); 
            /* Container Scrolling */ 
            self.scroll(node); 
            delete node; 
           } 
           return false; 
           break; 
          case 13: /* Enter */ 
           $element.blur() 
           if (resultIndex > -1) { 
            self.select($resultsItems[resultIndex]); 
           } 
           return false; 
           break 
         } 
         return true; 
        }); 
        /* Setup Menu Timeout */ 
        if (options.resultsTimeout > 0) { 
         menuTimeout = setTimeout(function() { 
          self.clear(); 
         },options.resultsTimeout); 
        } 
        /* User Callback: callResults */ 
        if (typeof(options.callResults) == 'function') { options.callResults(); } 
        /* User Callback: callComplete */ 
        if (typeof(options.callComplete) == 'function') { options.callComplete(); } 
       } else { 
        /* No Results */ 
        /* User Callback: callNoresults */ 
        if (typeof(options.callNoresults) == 'function') { options.callNoresults(); } 
        /* Inject Noresults Template */ 
        $element.after(self.templates.noresults(id+'-noresults', options.classNoresults)); 
        /* User Callback: callComplete */ 
        if (typeof(options.callComplete) == 'function') { options.callComplete(); } 
       } 
      }); 
     } 

     this.scroll = function(node) { 
      var self = this; 
      var viewport = { top: $('#'+self.id+'-results').scrollTop(), 
          bottom: ($('#'+self.id+'-results').scrollTop() + $('#'+self.id+'-results').height()), 
          height: ($('#'+self.id+'-results').scrollTop() + $('#'+self.id+'-results').height()) - $('#'+self.id+'-results').scrollTop() } 

      var pos = Napalm_Position.position(node); 
      var item = { top: $(node).prevAll().size() * (pos.bottom - pos.top), 
          bottom: ($(node).prevAll().size()+1) * (pos.bottom - pos.top), 
          height: pos.bottom - pos.top 
         } 
      delete pos; 

      /* Check Viewport Boundries */ 
      if (item.top < viewport.top) { /* Top */ 
       $('#'+id+'-results').scrollTop(item.top); 
      } else if (item.bottom > viewport.bottom) { /* Bottom */ 
       $('#'+id+'-results').scrollTop(item.bottom - viewport.height); 
      } 
     } 

     this.clear = function() { 
      $('#'+id+'-loading').remove(); 
      $('#'+id+'-results').remove(); 
      $('#'+id+'-noresults').remove(); 
      $('body').unbind('click'); 
      $(window).unbind('keydown'); 
      clearTimeout(menuTimeout); 
      typingStarted = false; 
      typingFinished = false; 
     } 

     this.reset = function() { 
      $element.show(); 
      $('#'+id+'-selected').remove(); 
      $element.focus(); 
     } 

     this.destroy = function() { 
      this.clear(); 
      this.reset(); 
      delete $element; 
      delete obj; 
      delete options; 
      delete componentName; 
      delete id; 
      delete menuTimeout; 
      delete keyTimeout; 
      delete typingStarted; 
      delete typingFinished; 
     } 

     this.templates = { 
      loading: function(_id, _class) { 
       return '<div id="'+_id+'" class="'+_class+'">' + 
         ' <img src="http://i.rebuild.sb.napalmriot.com/common/ajax/spinner2.gif" width="16" height="16" />' + 
         ' Searching..' + 
         '</div>'; 
      }, 
      noresults: function(_id, _class) { 
       return '<div id="'+_id+'" class="'+_class+'">' + 
         ' No games found matching your search' + 
         '</div>'; 
      }, 
      results: function(_id, _items, _class) { 
       return '<div id="'+_id+'" class="'+_class+'">'+ 
         ' <ul class="dieBullets">'+ 
         '  '+_items+ 
         ' </ul>'+ 
         '</div>'; 
      }, 
      resultsitem: function(_id, _content_id, _content_value) { 
       return '<li id="'+_id+'" rel="'+_content_id+'">'+ 
         ' '+_content_value+ 
         '</li>'; 
      }, 
      truncateditem: function(_id) { 
       return '<li id="'+_id+'" class="truncated">'+ 
         ' Refine your search<br />to see more results'+ 
         '</li>'; 
      }, 
      selected: function(_id, _content_id) { 
       var self = this; 

       var gameURL = ''; 
       var gameTitle = ''; 

       sendData = JSON.stringify({"gameid":_content_id}); 

       $.ajax({ 
        type:'GET', 
        async:false, 
        url:window.urls['component']+componentName+';getBoxartUrl;'+escape(escape(sendData)), 
        dataType:'json', 
        success:function(data) { 
         if (data.success) { 
          gameURL = data.response.url; 
          gameTitle = data.response.title; 
         } else { 
          Napalm_UI.error(data.response); 
         } 
        } 
       }); 

       if (options.infoBackground) { var bgcolor = options.infoBackground; } else { var bgcolor = ''; } 

       var html = '<div id="'+_id+'">'+ 
         ' <img src="'+gameURL+'" title="'+gameTitle+'" class="'+options.classBoxart+'" width="100" height="143" rel="'+_content_id+'" />'+ 
         ' <img src="http://i.napalmriot.com/boxart.php?id='+_content_id+'&bgcolor='+bgcolor+'" title="'+gameTitle+'" width="31" height="150" />'; 
       if (options.resultsChange) { 
        html += ' <br />'+ 
          ' <a href="#">'+options.resultsChangeText+'</a>'; 
       } 
       html +=  '</div>'; 

       delete gameURL; 
       delete gameTitle; 
       delete bgcolor; 
       delete _id; 
       delete _content_id; 

       return html; 
      } 
     }  

     /* Option Get/Set */ 
     this.option = function(key, value) { 
      if (typeof(options[key]) == 'undefined') { return false; } 
      if (typeof(value) == 'undefined') { return options[key]; } 
      if (options[key] = value) { return true; } 
      return false;    
     } 

     /* Option: SelectedId */ 
     if (options.selectedId !== false) { 
      this.select(options.selectedId); 
     } 

     this.binding(); 
    }; 
})(jQuery); 
+0

哇,墙代码。尽管如此,但大量的工作发布。 – hobodave 2009-07-31 16:10:24

回答

5

这是因为与delete的特殊性。

这是一个很难理解 - 甚至mozilla documentation使这个不清楚:

delete运算符删除对象, 对象的属性,或者在 在指定索引的元素阵列。

但后来指出

如果表达不计算为 财产,删除什么都不做。

但是,如果我们坚持读书,终于有些清晰度

可以使用delete运算符 删除声明暗示 但不是那些用var 声明声明的变量。

所以你去了。另请注意,根据操作的合法性,此页面提及的delete返回值为true | false。

下面这个小脚本复制类似的行为,以你的脚本,但在一个小的,容易看到的方式(需要萤火虫)

var a = {foo:'bar',bar:'foo',baz:1}; 
var b = {"a":a} 

console.log(a); 
console.log(b); 

delete a; 
delete b; 

console.log(a); 
console.log(b); 

delete b.a; 

console.log(b); 

总之,我认为您的解决方案将只是重置选项作为空对象

options = {}; 
+0

Ahhhhh,这是非常有价值的信息。谢谢!如果我可能对你施加第二个问题:在使用所有删除的destroy()方法之后,我要求jQ从元素中删除数据项。由于相同的“删除”问题,是否可以安全地假设不起作用?我确实假设jQ的数据方法只是一个元素名称 - >值堆栈。长话短说,有没有更好的办法,我应该打破选择和重新实施? – Spot 2009-07-31 17:21:35