Learning Javascript

  1. JavaScript is all about creating behaviours and data interactions by accessing the DOM.
  2. When using it? You want to make sure that he behavior you're trying to accomplish cannot be done better eith CSS.
  3. Content is king: It's the most important aspect of any website or application.
  4. Guiding principle: Progressive enhancement.
  5. Progressive enhancement: a layered apporach to web design, it creates a seperation between HTML(structure), CSS(presentation), and JavaSctipt(behavior), where it focus on content, the user, and accessibility. (Keep your layers in different files)
  6. Nothing can come in while javascript is loading to the browser. Moving your javascript documents to the bottom of the document will ensure that the entire document renders smoothly.
  7. Rendering: the browser translates the code into some form of coherent output. All browser does it differently. A rendering engine in a browser creates what you see on the screen. eg: Webkit(open source rendering engine)
  8. On certain CSS functions, prefacing with '-webkit-' will target all browsers that use the Webkit rendering engine.
  9. 3 main functions of JavaScript: 
    1. Modifying HTML
    2. Communicating with the server
    3. Storing data
  10. Know when to use CSS and when to use javascript. CSS renders much faster than JavaScript in the browser.
  11. Console: we use it for: debugging; monitoring errors; Ajax interaction.
  12. Tools: YUI Compressor (www.refresh-sf. com/yui/); JSLint (http://www.jslint.com) searches for bugs.
  13. Nodes in DOM has relationship as: parents, children, siblings.
  14. Variables in JavaScript can hold values, objects, or even functions. Local variables, global variables.
  15. global variable can be defined in 3 ways: 
    • define with "var" outside of a function.
    • define them anywhere without using "var"
    • adding something directly to the window object
  16. "use strict": the statement is inserted in to function definition, to make sure the parser uses stricter rules when executing your script.
  17. Caching in a JavaScript file usually refers to variables.
  18. Arrays: var family = ["father", "mother", "me", "sister"];
  19. Cookies: data stored locally in the browser, its best not rely too much on this type of storage for critical issues.
  20. JavaScript Object Notation(JSON): data format integrated with JavaScript. JSON will live in its own file and often on another server completely. It is currently the most common format for API services, and it was chosen because the human eye very easily reads it. It was originally thought to be an alternative to XML in data exchanges and quickly took over. 
  21. Object: 
  22. code sample:
     1 /*saving ...*/
     2 var people = family,    // family is an array
     3      peopleCount = family.length,
     4      i;
     5 
     6 /* checking ... */
     7 if(peopleCount > 0) {
     8     for(i = 0; i < peopleCount; i = i + 1){
     9         /*  blabla...  */
    10     }   
    11 }    
  23. switch
     1 switch (person) {
     2     case "tim":
     3            alert("this is me");
     4            break;
     5     case "christine":
     6            alert("my sister");
     7            break;
     8     default: 
     9            alert(person);   
    10 }
  24. Anonymous functions: functions that are dynamically declared at runtime. Use once, perform better. When function operator is called, it creates a new function operator and returns it. (function (msg) {alert(msg);})("so");
  25. Callback functions: when a function is passed into another function, it is called a callback function:
    1 window.addEventListener("load", function(){
    2                                           alert("call back function")
    3                                          }, false);

    anonymous functions can also be callback functions.

  26. When something is native to javascript or built into an external library, its usually called a method.(when a function is saved inside an object, it is refered to as method)
  27. Create a method for an object:
     1 var newObj = {
     2     met1 = function () {
     3         alert("hello!");   
     4     }
     5 }
     6 // met1 can be called like this:
     7 newObj.met1();
     8 // when declaring new object, use the object literal ({}), 
     9 // another way of defining an object using object constructor:
    10 function myObj () {
    11     this.method = function(){}
    12 }
    13 var o1 = new myObj();
    14 // compact:
    15 var o2 = {
    16     method : function()
    17 }
  28. event: 
    1 // execute the function on load of the window:
    2 window.addEventListener("load", getFamilyMemberNames, false);
    3 // execute the function on clicking the document:
    4 window.addEventListener("click", getFamilyMemberNames, false);
  29. The DOM is made up of items called nodes. Nodes: document node(document), element nodes, text nodes, attribute nodes. Attribute nodes are not considered to be children of their containing element(they are under element nodes).
    1. Targeting:
    2. 1 document.getElementById("someID");
      2 document.getElementsByTagName("p"); // this will return a DOM object which is an array of the //specified tag element. such as [<p>blablabla</p>, <p>blablabla</p>, <p>blabla</p>]
      3 // called NodeList, its a list of elements, and those elements stay the 
      4 // same order in the documents
      5 // returning the total number of elements in out NodeList:
      6 if(document.getElementsByTagName("p").length > 0){};
      7 // Targeting single node: first using item method:
      8 document.getElementsByTagName("p").item(0);
      9 // with array syntax:
      10 document.getElementsByTagName("p")[0];
      11 document.getElementsByClassName("");
      12 // <p class="drop huge">content</p>
      13 document.getElementsByClassName("drop huge");
  30. querySelector(), querySelectorAll(): querySelector() will return the first element encountered, while querySelectorAll() will return all
     1 <dive id="header">
     2     <h1>content</h1>
     3 </div>
     4 <p class="dropcap huge"></p>
     5 <p class="dropcap">content<span class="huge"></span></p>
     6 <p class="dropcap">content</p>
     7 // js:
     8 document.querySelector("#head");    // get the header ID element
     9 document.querySelector(".dropcap"); // get the first element with a dropcap //class
    10 document.querySelectorAll(".dropcap"); // get all elements with dropcap class
    11 document.querySelectorAll(".dropcap, .huge"); // get all elements with dropcat class or huge c12 // lass
    13 docuemtn.querySelectorAll("p[class]"); //get all paragraphs with a class
  31. Working with attribute node:
     1 getAttribute();
     2 setAttribute();
     3 removeAttribute();
     4 hasAttribute();  // return true if the specified attribute exists otherwise false
     5 //eg:
     6 document.getElementById("about").hasAttribute("class");  // true
     7 document.getElementById("about").getAttribute("class");  // return class name
     8 // css:
     9 .visible {
    10   position: static;
    11 }
    12 
    13 .hidden {
    14   position:  absolute;
    15   top:        -9999px;
    16   left:        -9999px;
    17 }
    18 // js:
    19 document.getElementById("plc").setAttribute("class", "hidden"); // first is class name and second is the value
  32. document.getElementById("target-area").innerHTML = "<p>hello world</p>";
    document.getElementById("plc").removeAttribute("class");
  33. parentNode; previousSibling; nextSibling; firstChild; lastChild:
    1 document.getElementById("about").parentNode.setAttribute("class", "active");
    2 document.getElementById("nav").firstChild.setAttribute("class", 'first");
    3 document.getElementById("nav").lastChild.setAttribute("class", "last");
  34. createElement([tagname]); CreateTextNode() take string as argument; appendChild() take one complete element you want to insert; removeChild():
    // before:
    <body>
      <div id="target-area">
        <p id="tagline">Hello World!</p>
      </div>
    </body>
    
    // js:
    var targetArea = document.getElementById("target-area");
    var p = createElement("p");
    var snippet = p.createTextNode("this was a generated paragraph");// create text node inside the <p>
    targetArea.appendChild(snippet); // insert our generated paragraph into the DOM

    remove a child node:

    var targetArea = document.getElementById("target-arez");
    var tagline = document.getElementById("tagline");
    targetArea.removeChild(tagline);
  35. Variables: string, number, boolean:(when declaring a variable, you are caching it in your JavaScript file)
    • Strings:
      • strings can be saved to variables with single or double quotes;
    • Numbers:
      • can be decimal, negative, positive, but not quoted(treated as string)
    • Boolean:
      • var stillHungry = true; // var stillHungry = false;(0 means false, 1 means true if used. 0 and 1 are also considered to be boolean values)
  36. Arrays: 
    • two ways of declaring:
      • var arr1 = new Array();
      • var arr2 = [];
    • declaring and assigning an array:
      • var arr3 = ["one", 2, false];
    • associative array: index value is set to be string other than number(not recommend this type of array)
      • var arr4 = []  // declare an array
      • arr4["one"] = "lunch"; arr4["two"] = "dinner";
    • multidimensional array: 
      1 var breakfast = ["egg", "milk", "water"];
      2 var lunch = ["steak", "pork", "vege"];
      3 var dinner = ["soup", "cakes", "banana"];
      4 var favoriteSandwishes = [breakfast, lunch, dinner];
      5 alert(favoriteSandwishes[0][1]);  // output milk
      6 breakfast.push("apple"); // add new item
  37. Array methods:
    • join, slice, shift, unshift, pop, concat, sort:
       1 var breakfast = ["one", "two", "three"];
       2 var joinbf = breakfast.join(", plus "); // #=> one, plus two, plus three
       3 // The join method will convert all items in an array to a string and output them in a format y 4 //ou specify.
       5 var slicebreakfast = breakfast.slice(1, 3);
       6 alert(slicebreakfast); // slice method take two argument, the first starting point is required  7 // and the second is optional(omitted, continue to the end)
       8 var sbreakfast = breakfast.shift(); // #=> one
       9 breakfast.unshift("one_1"); // add "one_1" to breakfast, return the length of the new array if  10 // assign it to a variable
       11 // shift and unshift are methods used to add and remove items to or from the beginning of spec 12 //ified array
       13 var pp = breakfast.pop(); // will return the last item in the array and remove it from the arr 14 //ay
       15 // concat:
       16 var a = [1, 2]; var b = ["one", "two"]; var c = ["x", "y"];
       17 var d = a.concat(b, c);
       18 alert(d); // #=> 1,2,one,two,x,y
       19 // concat: copying and combining multiple arrays, return a new array(original exists)
       20 // sort:
       21 var a = [30, 4, 3, 6];
       22 var d = a.sort();
       23 alert(d);  // #=> 3,30,4,6 because sort work by alphabetically
  38. Saving data in an object:
    1 var meatball = {
    2   bread: "Daiser Roll",
    3   meat: "Beef",
    4   cheese: "Mozzarella"
    5 }
    6 alert(meatball.bread);  // #=> Daiser Roll
  39. localStorage and sessionStorage(HTML5):
    1 // set localStorage:
    2 localStorage.setItem("favoriteSandwich", "meatball"); // the second argument must be string
    3 // get localStorage:
    4 var sandwich = localStorage.getItem("favoriteSandwich"); // returns meatball
    5 // delete stored data:
    6 localStorage.removeItem("favoriteSandwich");

    these two new storage objects are better than cookies. And both use the same sytax and methods, while localStorage will remain active until explicitly reomved by you or user via browser preferences. sessionStorage will remove itself of each browser session(when the browser is closed).

  40. Data saved in JSON object:
    var favoriteSandwiches = {
      "breakfast" : [
        {
            "name" : "Egg, Sausage and cheese",
            "bread" : "English Muffin"
        },
        {
            "name" : "Egg Whites on Flatbread",
            "bread" : "Flatbread"
        }   
      ],
      "lunch" : = [
        {
            "name" : "Turkey Club",
            "bread" : "Wheat bread"
        },
        {
            "name" : "Grilled cheese",
            "bread" : "White Bread"
        }
      ],
      "dinner" : [
        {
           "name" : "Meatball",
           "bread" : "Kaiser Roll"
        },
        {
           "name" : "Hamburger",
           "bread" : "Hamburger Roll"
        }
      ]
    };
    // go to dinner get the name of the first item
    alert(favoriteSandwiches.dinner[0].name);  // meatball
    // stringify the JSON object:
    var stringObj = JSON.stringify(favoriteSandwiches);
    // add the string object to localStorage:
    localStorage.setItem("favoriteSandwiches", stringObj);

     get data use JSON.parse:

    1 // get the locally stored data:
    2 var storedItem = localStorage.getItem("favoriteSandwiches");
    3 // convert it from a string, back into a JSON object:
    4 var convertObj = JSON.parse(storedItem);
    5 // prove it worked:
    6 alert(convertObj.breakfast[0].name);  // #=> Egg, sausage and cheese

    browser support for the new feature with HTML5:

  41. feature detection: it will be a main theme whenever we're coding for these new standards brought forth by HTML5:
    // checking for localStorage support:
    if(typeof(localStorage) === "undefined") {
      // localStorage is not supported, maybe use cookies?
    } else {
      // localStorage is supported, use it here
    }
  42. Grouping variables: when writing an application, its best to try to group all variables at the top of your js file or function(small performance boosts)
    1 // variable declaration with single var:
    2 var highSchool = "Hill",
    3     college = "Paul",
    4     gradSchool = "Vishaal";
  43. Reserved words:
    break;    case;    catch;    continue;    debugger;    default;    delete;
    do;       else;    finally;    for;       function;    if;         implements;
    in;       instanceof;    interface;    new;    package;  private;  protected;
    public; static;
    return; switch; this; throw; try; typeof;
    var; void; while; with
  44. Creating functions:
    1 function sayHello() {
    2     alert("Hey, there!");
    3 }
    4 // calling it:
    5 sayHello();  // Hey, there!
  45. Anonymous function: functions that have no name. They are dynamic(executed at runtime rather than waiting to be called). Defining an anonymous function:
    1 (function() {
    2   // define a variable inside the function:
    3   var greeting = "Hello, Tim";
    4   alert("In scope: " + greeting);
    5 })();
    6 // scope:
    7 alert("out of scope: " + typeof(greeting)); // alerts "undefined"
  46. callback function: a function calls another function, the second is a callback function.(callback functions passed as arguments to other functions):
     1 function sayHello(message){
     2   alert(message);
     3 }  // used as callback function
     4 
     5 (function(){
     6   var greeting = "Welcome",
     7       exitStatement = "ok, please to leave";
     8 
     9   sayHello(greeting);
    10   sayHello(exitStatement);
    11 })();
  47. Use === instead of == and you won’t hit that weird gray area of false versus 0 and true versus 1 when dealing with Booleans. 
  48. in the switch statement, the default option is required. If you have a lot of options, better use switch.
  49. event-driven javascript:
    • Get a DOM node
    • Attach an event to it
    • Execute a specific function when the event is triggered
  50. Event handlers: every event is on<event> when using event handlers(onsubmit, onchange, onfocus, onblur, on mouseover, onmouseout, ontouchstart, onguesturestart...). The one issue with event handler is that you can attach only a single function to a specific event of a DOM node.(event listener is highly recommended)
     1 (function(){
     2   // HTML: <button type="button" id="btn">a button</button>
     3   // save the button to a variable:
     4   var btn = document.getElementById("btn");
     5 
     6   // use event handler to attach the onclick event:
     7   btn.onclick = function(){
     8     alert("click the button");
     9   }
    10 })();
  51. Event listener
     1 element.addEventListener("event", function(){
     2     // stuff you want the function to do
     3 }, false)
     4 // example:
     5 (function(){
     6   // HTML as event handler one
     7   var btn = document.getElementById("btn");
     8   btn.addEventListener("click", function(){
     9       alert("clicked the button");
    10   }, false);
    11 })();
  52. fallback if addEventListener is not supported:
     1 if(addEventListener){
     2   // do some stuff with addEventListener
     3   btn.addEventListener("click", function(){
     4     alert("clicked the button");
     5   }, false);
     6 } else {
     7   // if not supported(IE8 or below)
     8   element.attachEvent("click", function(){
     9     alert("clicked button");
    10   });
    11 }
  53. When a function is inside an addEventListener() method, it’s called an event listener. Binding event to DOM:
    1 var btn = document.getElementById("btn");
    2 function alertMessage(){
    3   alert("clicked the button");
    4 }
    5 
    6 btn.addEventListener("clicked", alertMessage, false);

    In order to pass arguments into a function while using addEventListener(), you need to use the function as a callback instead by nesting it inside an anonymous function:

    1 var btn = document.getElementById("btn");
    2 function alertMessage(message){
    3   alert(message);
    4 }
    5 
    6 btn.addEventListener("click", function(){
    7   alertMessage("clicked the button");
    8 }, false);

    unbinding event:

    1 if(btn.removeEventListener){
    2   // if removeEventListener is supported
    3   btn.removeEventListener("click", alertMessage, false);
    4 } else {
    5   // if not supported
    6   btn.detachEvent("click", alertMessage);
    7 }
  54. focus and blur: think of focus and blur as click and unclick, how to tell if an element is focusable? Tab through a page, if you can reach the element you want, then it is focusable. Link and form input elements (text inputs, radio button, check boxes, buttons) are all focusable elements. When a user focuses on the search field, we are going to add a class of "active" on the parent element:
     1 addActiveSection : function(){
     2     this.parentNode.setAttribute("class", "active");
     3 }
     4 removeActiveSection : function(){
     5     this.parentNode.removeAttribute("class");
     6 }
     7 
     8 // CSS snippet:
     9 .active {
    10   background:    #ddd;
    11 }
    12 .hovering {
    13   background:    #eee;
    14 }
    15 form > div {
    16   padding:          10px;
    17 }
    18 // other code:
    19 var searchField = document.getElementById("q");
    20 searchField.addEventListener("click", adr.addActiveSection, false);
    21 searchField.addEventListener("blur", adr.removeActiveSection, false);
  55. thishttp://www.digital-web.com/articles/scope_in_javascript/https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this.
    • Global context: in the global execution context, this refers to the global object, whether in strict mode or not
      1 console.log(this.document === document); // true
      2 console.log(this === window);                   // true
      3 this.a = 37;
      4 console.log(window.a);                              // 37    
    • Function call:
       1 function f1(){
       2   return this;
       3 }
       4 f1() === window  // true
       5 
       6 function f2(){
       7   "use strict";
       8   return this;
       9 }
      10 f2() === undefined // firefox, in chrome, it return window object
    • As an object method: when a function is called as a method of an object, its this is set to the object the method is called on
      1 var o = {
      2   porp: 37;
      3   f : function(){
      4     return this.prop;
      5   }   
      6 }
      7 
      8 console.log(o.f());    // logs 37
  56. 3 ways to activate a search box:
    • Click it
    • Click the label next to it
    • Tab to navigate
  57. indexOf() function: The indexOf() method returns the position of the first occurrence of a specified value in a string. This method returns -1 if the value to search for never occurs.
  58. onkeypress, onkeyup, onkeydown: as I tested in chrome and firefox, onkeypress shows the character you press, and will not keep going as onkeydown does, onkeyup will show up character when you release the key, you can try it out:
    1 <input type="text" onkeypress="this.value+='Onkeypress'"></input>
    2 <input type="text" onkeyup="this.value+='Onkeyup'"></input>
    3 <input type="text" onkeydown="this.value+='Onkeydown'"></input>
  59. touchstart, touchend, touchmoveorientationchange event:
    1 var body = document.getElementsByTagName("body")[0];
    2 body.addEventListener("touchstart", touchControls.pokeTheScreen, false);
    3 body.addEventListener("touchend", touchControls.stopPokingTheScreen, false);
  60. Ajax: Ajax is a server communication technology, it is the concept of refreshing a part of an HTML document without reloading the entire page. (Asynchronous JavaScript + XML). Creating an instance of XMLHttpRequest:
     1 function getHTTPObject(){
     2     // initialize the variable
     3     var xhr;        
     4     // check for XMLHttpRequest support
     5     if(XMLHttpRequest){  // check for support
     6         xhr = newXMLHttpRequest();
     7     } else if(window.ActiveXObject){  // check for IE 6 version
     8         xhr = new ActiveXObject("Msxm112.XMLHTTP");
     9     }
    10     return xhr;
    11 }
    12 var request = getHTTPObject();
    13 request.open("GET", "data/contacts.json", true); // method; the file or URL to get; A Boolean flag for asynchronous script.
  61. open(): 
    • argument:
      • Method:
        • GET or POST  // used for Ajax call
      • File or URL:
        • The file or URL argument is the place for the file path or full HTTP URL of the data source you will be pulling in via Ajax call. If its a local file, the path is relative to the HTML document you're using it in.
      • synchronous call or an asynchronous(the third argument), this will be set to true almost all the time, because asynchronous Ajax calls usually provide a much better user experience when compared to synchronous calls. 
  62. time interval:
    1 var myTimer = setInterval('alert("5 seconds past")', 5000); // the first argument is a function you want to repeat, second argument is time measured in millisecond.
    2 clearInterval(myTimer);    // clear interval
  63. polling: 轮询, the process of constantly hitting the server to check for new infomation is called polling.
  64. closure: the concept of variable scope traveling downhill(cascading if you will, to use a CSS term) is called closure.
  65. A key aspect of creating a high-performance Web application is to cut down the HTTP requests as much as possible. Concatenation: server-side script to concatenate js files into one to reduce the number of HTTP requests.
  66. indentation: as industry standard,  use 4 spaces instead of tab for indentation.
  67. A project has two kind of files: development file and production file. The development files will contain all the ample whitespace, formatting, and comments you need to effectively work. While in production file, all whitespaces and comments are removed, and the file shrunk to a single line.
  68. As a general rule, parentheses should have a space before and a space after. The comma creates a natural coding breakpoint in JavaScript. Other operators like +, –, or = are also natural breakpoints. Each operator should have a space before and after.  
  69. eval() is terrible, don't use eval();
  70. adding a second method to an object is a process similar to creating a comma-separated list of variables:
    1 var party = {
    2     pizza : function(){
    3         // this is a function        
    4     },
    5     beverage : function(){
    6         // another function
    7     }   
    8 }
  71. Libraries, Toolkits and Framework: Libraries and toolkits are the most common, because they are seen as a giant ball of (well-organized) functions and methods for you to reuse. Libraries generally come in the form of a single file you attach to your HTML document. Some toolkits come in the form of a few separate files that are conditionally included into the document. Frameworks are much more than simple libraries; they can contain libraries (like one that we will go over) but they speak to an overall structural change in the way you code JavaScript, offering many more tools. 
  72. Popular libraries and framework:
    • YUI(yuilibrary.com):
      • YUI as a whole is called a framework because it contains the YUI JavaScript Library, the YUI CSS Library, and other tools such as the YUI Online Code Compressor, which will safely minify your code for you. 
    • jQuery:
      • most popular library.(its still just javascript):
        <!--js-->
        <script src="js/jquery.js"></script>
        <script src="js/script.js"></script>
  73. Selecting an element by ID:
    1 // native javascript version
    2 var container = document.getElementById("container");
    3 // native javascript newer-version
    4 var container = document.querySelector("#container");
    5 //jQuery version
    6 var container = $("container");
  74. Selecting an element by class name:
    // native javascript version
    var module = document.getElementByClassName("module");
    // native javascript newer-version
    var module = document.querySelector(".module");
    // native javascript newer-version, return array
    var module = document.querySelectorAll(".module");
    // jQuery version
    var module = $(".module");
  75. When you animate the DOM node, you're dynamically updating CSS properties.
  76. Good part of libraries:
    • fix the cross-browser issues inherent to JavaScript
    • popularity and community
    • code efficiency
  77. Bad part of libraries:
    • they are large, overhead
  78. CDN: Content Delivery Network, is a large, distributed collection of servers that deliver content based on a user’s geographical location. Using CDN to serve up content to users based on where they are is considered to be a best practice in Web development. If content is sent to a user from a geographically closer location, it stands to reason that the content will reach the user faster:
    1 // Linking to the Google Hosted jQuery:
    2 <!--loading jQuery from the Google CDN-->
    3 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    4 <script src="js/script.js"></script>
    5 </body>
    6 </html>
  79. It is important to remember that everything in the library is just a reference to a method that is native to JavaScript; 
  80. The navitator object: returns some helpful information(Browser name, version; Platform; if js enabled...)
  81. Geolocation:
     1 <!DOCTYPE html>
     2 <html>
     3 <body>
     4 <p id="demo">Click the button to get your coordinates:</p>
     5 <button onclick="getLocation()">Try It</button>
     6 <script>
     7 var x=document.getElementById("demo");
     8 function getLocation()
     9   {
    10   if (navigator.geolocation)
    11     {
    12     navigator.geolocation.getCurrentPosition(showPosition);
    13     }
    14   else{x.innerHTML="Geolocation is not supported by this browser.";}
    15   }
    16 function showPosition(position)
    17   {
    18   x.innerHTML="Latitude: " + position.coords.latitude + 
    19   "<br>Longitude: " + position.coords.longitude;    
    20   }
    21 </script>
    22 </body>
    23 </html>
  82. Web workers: Web workers help alleviate that problem by opening up a secondary thread in which you can pass data back and forth through. Web workers live in a separate JavaScript file, which is refer- enced from the main JavaScript file. Because of security concerns, workers do not have access to the DOM; their sole purpose is to fetch and return data to the main file. (http://www.html5rocks.com/en/tutorials/workers/basics/)
  83. n
  84. n
  85. n
  86. n
  87. n
  88. n
  89. n
  90. n
  91. n
  92. n
  93. n
  94. n
  95. n
  96. n
posted @ 2013-05-30 02:37  wxwcase  阅读(342)  评论(0编辑  收藏  举报