Напишем HTML парсер на JavaScript.
Наша парсер будет проверять:
- отсутствие закрывающих тегов:
<p><b>Hello → <p><b>Hello</b></p>
- отсутствие закрывающего слэша в «самозакрывающихся» элементах:
<img src=test.jpg> → <img src="test.jpg"/>
- незакрытый строчный элемент перед блочным:
<b>Hello <p>John → <b>Hello </b><p>John</p>
- отсутствие закрывающих тегов у элементов, для которых это допустимо в HTML4:
<p>Hello<p>World → <p>Hello</p><p>World</p>
- атрибуты без значений (флаги):
<input disabled> → <input disabled="disabled"/>
- ошибочный порядок закрывающих тегов вложенных элементов:
<b><i>example</b></i> → <b><i>example</i></b>
HTMLпарсер буде работать следующим образом:
SAX-style API
Управление тэгами, текстом и комментариями с помощью функций обратного вызова:
var results = "";
HTMLParser("<p id=test>hello <i>world", {
start: function( tag, attrs, unary ) {
results += "<" + tag;
for ( var i = 0; i < attrs.length; i++ )
results += " " + attrs[i].name + '="' + attrs[i].escaped + '"';
results += (unary ? "/" : "") + ">";
},
end: function( tag ) {
results += "</" + tag + ">";
},
chars: function( text ) {
results += text;
},
comment: function( text ) {
results += "<!--" + text + "-->";
}
});
results == '<p id="test">hello <i>world</i></p>"
XML Serializer
var results = HTMLtoXML("<p>Data: <input disabled>")
results == '<p>Data: <input disabled="disabled"/></p>'
DOM Builder
// The following is appended into the document body
HTMLtoDOM("<p>Hello <b>World", document)
// The follow is appended into the specified element
HTMLtoDOM("<p>Hello <b>World", document.getElementById("test"))
DOM Document Creator
var dom = HTMLtoDOM("<p>Data: <input disabled>");
dom.getElementsByTagName("body").length == 1
dom.getElementsByTagName("p").length == 1
While this library doesn’t cover the full gamut of possible weirdness that HTML provides, it does handle a lot of the most obvious stuff. All of the following are accounted for:
Код HTML парсера на JavaScript:
/*
* HTML Parser By John Resig (ejohn.org)
* Original code by Erik Arvidsson, Mozilla Public License
* http: // erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* // Use like so:
* HTMLParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* // or to get an XML string:
* HTMLtoXML(htmlString);
*
* // or to get an XML DOM Document
* HTMLtoDOM(htmlString);
*
* // or to inject into an existing document/DOM node
* HTMLtoDOM(htmlString, document);
* HTMLtoDOM(htmlString, document.body);
*
*/
(function(){
// Regular Expressions for parsing tags and attributes
var startTag = /^<([-A-Za-z0-9_]+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
attr = /([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
// Empty Elements - HTML 4.01
var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
// Block Elements - HTML 4.01
var block = makeMap("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");
// Inline Elements - HTML 4.01
var inline = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
// Elements that you can, intentionally, leave open
// (and which close themselves)
var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
// Attributes that have their values filled in disabled="disabled"
var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
// Special Elements (can contain anything)
var special = makeMap("script,style");
var HTMLParser = this.HTMLParser = function( html, handler ) {
var index, chars, match, stack = [], last = html;
stack.last = function(){
return this[ this.length - 1 ];
};
while ( html ) {
chars = true;
// Make sure we're not in a script or style element
if ( !stack.last() || !special[ stack.last() ] ) {
// Comment
if ( html.indexOf("<!--") == 0 ) {
index = html.indexOf("-->");
if ( index >= 0 ) {
if ( handler.comment )
handler.comment( html.substring( 4, index ) );
html = html.substring( index + 3 );
chars = false;
}
// end tag
} else if ( html.indexOf("</") == 0 ) {
match = html.match( endTag );
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( endTag, parseEndTag );
chars = false;
}
// start tag
} else if ( html.indexOf("<") == 0 ) {
match = html.match( startTag );
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( startTag, parseStartTag );
chars = false;
}
}
if ( chars ) {
index = html.indexOf("<");
var text = index < 0 ? html : html.substring( 0, index );
html = index < 0 ? "" : html.substring( index );
if ( handler.chars )
handler.chars( text );
}
} else {
html = html.replace(new RegExp("(.*)<\/" + stack.last() + "[^>]*>"), function(all, text){
text = text.replace(/<!--(.*?)-->/g, "$1")
.replace(/<!\[CDATA\[(.*?)]]>/g, "$1");
if ( handler.chars )
handler.chars( text );
return "";
});
parseEndTag( "", stack.last() );
}
if ( html == last )
throw "Parse Error: " + html;
last = html;
}
// Clean up any remaining tags
parseEndTag();
function parseStartTag( tag, tagName, rest, unary ) {
tagName = tagName.toLowerCase();
if ( block[ tagName ] ) {
while ( stack.last() && inline[ stack.last() ] ) {
parseEndTag( "", stack.last() );
}
}
if ( closeSelf[ tagName ] && stack.last() == tagName ) {
parseEndTag( "", tagName );
}
unary = empty[ tagName ] || !!unary;
if ( !unary )
stack.push( tagName );
if ( handler.start ) {
var attrs = [];
rest.replace(attr, function(match, name) {
var value = arguments[2] ? arguments[2] :
arguments[3] ? arguments[3] :
arguments[4] ? arguments[4] :
fillAttrs[name] ? name : "";
attrs.push({
name: name,
value: value,
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
});
});
if ( handler.start )
handler.start( tagName, attrs, unary );
}
}
function parseEndTag( tag, tagName ) {
// If no tag name is provided, clean shop
if ( !tagName )
var pos = 0;
// Find the closest opened tag of the same type
else
for ( var pos = stack.length - 1; pos >= 0; pos-- )
if ( stack[ pos ] == tagName )
break;
if ( pos >= 0 ) {
// Close all the open elements, up the stack
for ( var i = stack.length - 1; i >= pos; i-- )
if ( handler.end )
handler.end( stack[ i ] );
// Remove the open elements from the stack
stack.length = pos;
}
}
};
this.HTMLtoXML = function( html ) {
var results = "";
HTMLParser(html, {
start: function( tag, attrs, unary ) {
results += "<" + tag;
for ( var i = 0; i < attrs.length; i++ )
results += " " + attrs[i].name + '="' + attrs[i].escaped + '"';
results += (unary ? "/" : "") + ">";
},
end: function( tag ) {
results += "</" + tag + ">";
},
chars: function( text ) {
results += text;
},
comment: function( text ) {
results += "<!--" + text + "-->";
}
});
return results;
};
this.HTMLtoDOM = function( html, doc ) {
// There can be only one of these elements
var one = makeMap("html,head,body,title");
// Enforce a structure for the document
var structure = {
link: "head",
base: "head"
};
if ( !doc ) {
if ( typeof DOMDocument != "undefined" )
doc = new DOMDocument();
else if ( typeof document != "undefined" && document.implementation && document.implementation.createDocument )
doc = document.implementation.createDocument("", "", null);
else if ( typeof ActiveX != "undefined" )
doc = new ActiveXObject("Msxml.DOMDocument");
} else
doc = doc.ownerDocument ||
doc.getOwnerDocument && doc.getOwnerDocument() ||
doc;
var elems = [],
documentElement = doc.documentElement ||
doc.getDocumentElement && doc.getDocumentElement();
// If we're dealing with an empty document then we
// need to pre-populate it with the HTML document structure
if ( !documentElement && doc.createElement ) (function(){
var html = doc.createElement("html");
var head = doc.createElement("head");
head.appendChild( doc.createElement("title") );
html.appendChild( head );
html.appendChild( doc.createElement("body") );
doc.appendChild( html );
})();
// Find all the unique elements
if ( doc.getElementsByTagName )
for ( var i in one )
one[ i ] = doc.getElementsByTagName( i )[0];
// If we're working with a document, inject contents into
// the body element
var curParentNode = one.body;
HTMLParser( html, {
start: function( tagName, attrs, unary ) {
// If it's a pre-built element, then we can ignore
// its construction
if ( one[ tagName ] ) {
curParentNode = one[ tagName ];
if ( !unary ) {
elems.push( curParentNode );
}
return;
}
var elem = doc.createElement( tagName );
for ( var attr in attrs )
elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] != "boolean" )
one[ structure[ tagName ] ].appendChild( elem );
else if ( curParentNode && curParentNode.appendChild )
curParentNode.appendChild( elem );
if ( !unary ) {
elems.push( elem );
curParentNode = elem;
}
},
end: function( tag ) {
elems.length -= 1;
// Init the new parentNode
curParentNode = elems[ elems.length - 1 ];
},
chars: function( text ) {
curParentNode.appendChild( doc.createTextNode( text ) );
},
comment: function( text ) {
// create comment node
}
});
return doc;
};
function makeMap(str){
var obj = {}, items = str.split(",");
for ( var i = 0; i < items.length; i++ )
obj[ items[i] ] = true;
return obj;
}
})();
четверг, 21 ноября 2013 г.
среда, 20 ноября 2013 г.
Оптимизируем getElementById
Данный метод пригодится для AJAX-приложений, где часто приходится обращаться к элементам по id. Оптимизировать код можно кэшируя элементы, что приводит к серьезному приросту. В результате вы получите увеличение скорости работы кода в 3 раза.
function $ (id) {
return !$.cache[id] ? $.cache[id] = document.getElementById(id) : $.cache[id];
}
$.cache = [];
function $ (id) {
return !$.cache[id] ? $.cache[id] = document.getElementById(id) : $.cache[id];
}
$.cache = [];
JavaScript Очень простой пример наследования классов от Microsoft из TypeScript
Базовое наследование классов в JavaScript.
var extend = this.extend || function (childClass, parentClass) {
for (var key in parentClass) {
if (parentClass.hasOwnProperty(key)) {
childClass[key] = parentClass[key];
}
}
function F () {
this.constructor = childClass;
}
F.prototype = parentClass.prototype;
childClass.prototype = new F();
};
var ParentClass = (function () {
// Constructor
function ParentClass(message) {
// Constructor body
this.greeting = message;
this.x = 1;
this.y = 2;
this.z = 3;
}
// Public function
ParentClass.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return ParentClass;
})();
var ChildClass = (function (parentClass) {
// Extend
extend(ChildClass, parentClass);
// Constructor
function ChildClass(message) {
parentClass.apply(this, arguments); // или можно сделать частично parentClass.call(this, message);
this.greeting = message;
}
// Public variable
ChildClass.prototype.name = 'Parent';
// Public function
ChildClass.prototype.greet = function () {
return 'Hello, ' + this.greeting;
};
// Public function
ChildClass.prototype.hey = function () {
return 'Hey! ' + privateFunc();
};
// Private variable
var privateVar = 'Hello';
// Private function
function privateFunc () {
return privateVar + ' my friend!';
}
// Static variable
ChildClass.staticVar = 'I am static variable';
// Static function
function staticFunc () {
return 'I am static function';
}
return ChildClass;
})(ParentClass);
var SubClass = (function (parentClass) {
extend(SubClass, parentClass);
function SubClass(message) {
parentClass.apply(this, arguments);
this.greeting = message;
}
SubClass.prototype.greet = function () {
return 'Hello, ' + this.greeting;
};
SubClass.prototype.hey = function () {
return 'Hey!';
};
return SubClass;
})(ChildClass);
var parentObject = new ParentClass('world');
var childObject = new ChildClass();
var subObject = new SubClass();
console.log(parentObject.greet());
console.log(childObject.hey());
console.log(subObject.hey());
Примеси - Mixins
function applyMixins(derivedCtor, baseCtors) {
baseCtors.forEach(function (baseCtor) {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(function (name) {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
// Disposable Mixin
var Disposable = (function () {
function Disposable() {
}
Disposable.prototype.dispose = function () {
this.isDisposed = true;
};
return Disposable;
})();
// Activatable Mixin
var Activatable = (function () {
function Activatable() {
}
Activatable.prototype.activate = function () {
this.isActive = true;
};
Activatable.prototype.deactivate = function () {
this.isActive = false;
};
return Activatable;
})();
var SmartObject = (function () {
function SmartObject() {
var _this = this;
// Disposable
this.isDisposed = false;
// Activatable
this.isActive = false;
setInterval(function () {
return console.log(_this.isActive + " : " + _this.isDisposed);
}, 500);
}
SmartObject.prototype.interact = function () {
this.activate();
};
return SmartObject;
})();
applyMixins(SmartObject, [Disposable, Activatable]);
var smartObj = new SmartObject();
setTimeout(function () {
return smartObj.interact();
}, 1000);
Пример работы со статичными переменными и функциями внутри класса.
var Point = (function () {
// Object constructor
function Point(x, y) {
this.x = x;
this.y = y;
}
// Public function
Point.prototype.distance = function (p) {
var dx = this.x - p.x;
var dy = this.y - p.y;
return Math.sqrt(dx * dx + dy * dy);
};
// Static function
Point.distance = function (p1, p2) {
return p1.distance(p2);
};
// Static variable
Point.origin = new Point(0, 0);
return Point;
})();
Пример вызова родительского класса в дочернем классе.
var Point = (function () {
// Constructor
function Point(x, y) {
this.x = x;
this.y = y;
}
// Public function
Point.prototype.toString = function () {
return 'x=' + this.x + ' y=' + this.y;
};
return Point;
})();
var ColoredPoint = (function (parentClass) {
// Extend parent class
extend(ColoredPoint, parentClass);
// Constructor
function ColoredPoint(x, y, color) {
parentClass.apply(this, arguments);
parentClass.call(this, x, y);
this.color = color;
}
// Public function
ColoredPoint.prototype.toString = function () {
return parentClass.prototype.toString.call(this) + ' color=' + this.color;
};
return ColoredPoint;
})(Point);
Пример наследования класса во множестве дочерних классов.
var Control = (function () {
function Control() {}
return Control;
})();
var Button = (function (parentClass) {
extend(Button, parentClass);
function Button() {
parentClass.apply(this, arguments);
}
Button.prototype.select = function () {};
return Button;
})(Control);
var TextBox = (function (parentClass) {
extend(TextBox, parentClass);
function TextBox() {
parentClass.apply(this, arguments);
}
TextBox.prototype.select = function () {};
return TextBox;
})(Control);
var Image = (function (parentClass) {
extend(Image, parentClass);
function Image() {
parentClass.apply(this, arguments);
}
return Image;
})(Control);
Public, Private, и Privileged переменные и функции.
Public
function Constructor(...) {
this.membername = value;
}
Constructor.prototype.membername = value;
Private
function Constructor(...) {
var that = this;
// Private
var membername = value;
function membername(...) {...}
}
Примечание: данное выражение
function membername(...) {...}
это сокращенная запись такого выражения
var membername = function membername(...) {...};
Privileged
function Constructor(...) {
this.membername = function (...) {...};
}
Пример.
function Constructor (param) {
// Public variable
this.member = param;
// Private variables. They are not accessible to the outside, nor are they accessible to the object's own public methods. They are accessible to private functions.
var secret = 3;
var that = this;
// Private function inside constructor. Private methods cannot be called by public methods. It can be used to make this object limited to 3 uses. To make private methods useful, we need to introduce a privileged method.
function dec() {
if (secret > 0) {
secret -= 1;
return true;
} else {
return false;
}
}
// Privileged function. A privileged method is able to access the private variables and methods, and is itself accessible to the public methods and the outside. It is possible to delete or replace a privileged method, but it is not possible to alter it, or to force it to give up its secrets.
this.service = function () {
return dec() ? that.member : null;
};
}
Пример модуля.
var M;
(function (M) {
var s = "hello";
function f() {
return s;
}
M.f = f;
})(M || (M = {}));
M.f();
Пример экспортирования значений в модуль.
(function (exports) {
var key = generateSecretKey();
function sendMessage(message) {
sendSecureMessage(message, key);
}
exports.sendMessage = sendMessage;
})(MessageModule);
Загрузка модулей осуществляется в стиле Require JS.
define(["require", "exports", "log"], function(require, exports, log) {
log.message("hello");
});
var extend = this.extend || function (childClass, parentClass) {
for (var key in parentClass) {
if (parentClass.hasOwnProperty(key)) {
childClass[key] = parentClass[key];
}
}
function F () {
this.constructor = childClass;
}
F.prototype = parentClass.prototype;
childClass.prototype = new F();
};
var ParentClass = (function () {
// Constructor
function ParentClass(message) {
// Constructor body
this.greeting = message;
this.x = 1;
this.y = 2;
this.z = 3;
}
// Public function
ParentClass.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return ParentClass;
})();
var ChildClass = (function (parentClass) {
// Extend
extend(ChildClass, parentClass);
// Constructor
function ChildClass(message) {
parentClass.apply(this, arguments); // или можно сделать частично parentClass.call(this, message);
this.greeting = message;
}
// Public variable
ChildClass.prototype.name = 'Parent';
// Public function
ChildClass.prototype.greet = function () {
return 'Hello, ' + this.greeting;
};
// Public function
ChildClass.prototype.hey = function () {
return 'Hey! ' + privateFunc();
};
// Private variable
var privateVar = 'Hello';
// Private function
function privateFunc () {
return privateVar + ' my friend!';
}
// Static variable
ChildClass.staticVar = 'I am static variable';
// Static function
function staticFunc () {
return 'I am static function';
}
return ChildClass;
})(ParentClass);
var SubClass = (function (parentClass) {
extend(SubClass, parentClass);
function SubClass(message) {
parentClass.apply(this, arguments);
this.greeting = message;
}
SubClass.prototype.greet = function () {
return 'Hello, ' + this.greeting;
};
SubClass.prototype.hey = function () {
return 'Hey!';
};
return SubClass;
})(ChildClass);
var parentObject = new ParentClass('world');
var childObject = new ChildClass();
var subObject = new SubClass();
console.log(parentObject.greet());
console.log(childObject.hey());
console.log(subObject.hey());
Примеси - Mixins
function applyMixins(derivedCtor, baseCtors) {
baseCtors.forEach(function (baseCtor) {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(function (name) {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
// Disposable Mixin
var Disposable = (function () {
function Disposable() {
}
Disposable.prototype.dispose = function () {
this.isDisposed = true;
};
return Disposable;
})();
// Activatable Mixin
var Activatable = (function () {
function Activatable() {
}
Activatable.prototype.activate = function () {
this.isActive = true;
};
Activatable.prototype.deactivate = function () {
this.isActive = false;
};
return Activatable;
})();
var SmartObject = (function () {
function SmartObject() {
var _this = this;
// Disposable
this.isDisposed = false;
// Activatable
this.isActive = false;
setInterval(function () {
return console.log(_this.isActive + " : " + _this.isDisposed);
}, 500);
}
SmartObject.prototype.interact = function () {
this.activate();
};
return SmartObject;
})();
applyMixins(SmartObject, [Disposable, Activatable]);
var smartObj = new SmartObject();
setTimeout(function () {
return smartObj.interact();
}, 1000);
Пример работы со статичными переменными и функциями внутри класса.
var Point = (function () {
// Object constructor
function Point(x, y) {
this.x = x;
this.y = y;
}
// Public function
Point.prototype.distance = function (p) {
var dx = this.x - p.x;
var dy = this.y - p.y;
return Math.sqrt(dx * dx + dy * dy);
};
// Static function
Point.distance = function (p1, p2) {
return p1.distance(p2);
};
// Static variable
Point.origin = new Point(0, 0);
return Point;
})();
Пример вызова родительского класса в дочернем классе.
var Point = (function () {
// Constructor
function Point(x, y) {
this.x = x;
this.y = y;
}
// Public function
Point.prototype.toString = function () {
return 'x=' + this.x + ' y=' + this.y;
};
return Point;
})();
var ColoredPoint = (function (parentClass) {
// Extend parent class
extend(ColoredPoint, parentClass);
// Constructor
function ColoredPoint(x, y, color) {
parentClass.apply(this, arguments);
parentClass.call(this, x, y);
this.color = color;
}
// Public function
ColoredPoint.prototype.toString = function () {
return parentClass.prototype.toString.call(this) + ' color=' + this.color;
};
return ColoredPoint;
})(Point);
Пример наследования класса во множестве дочерних классов.
var Control = (function () {
function Control() {}
return Control;
})();
var Button = (function (parentClass) {
extend(Button, parentClass);
function Button() {
parentClass.apply(this, arguments);
}
Button.prototype.select = function () {};
return Button;
})(Control);
var TextBox = (function (parentClass) {
extend(TextBox, parentClass);
function TextBox() {
parentClass.apply(this, arguments);
}
TextBox.prototype.select = function () {};
return TextBox;
})(Control);
var Image = (function (parentClass) {
extend(Image, parentClass);
function Image() {
parentClass.apply(this, arguments);
}
return Image;
})(Control);
Public, Private, и Privileged переменные и функции.
Public
function Constructor(...) {
this.membername = value;
}
Constructor.prototype.membername = value;
Private
function Constructor(...) {
var that = this;
// Private
var membername = value;
function membername(...) {...}
}
Примечание: данное выражение
function membername(...) {...}
это сокращенная запись такого выражения
var membername = function membername(...) {...};
Privileged
function Constructor(...) {
this.membername = function (...) {...};
}
Пример.
function Constructor (param) {
// Public variable
this.member = param;
// Private variables. They are not accessible to the outside, nor are they accessible to the object's own public methods. They are accessible to private functions.
var secret = 3;
var that = this;
// Private function inside constructor. Private methods cannot be called by public methods. It can be used to make this object limited to 3 uses. To make private methods useful, we need to introduce a privileged method.
function dec() {
if (secret > 0) {
secret -= 1;
return true;
} else {
return false;
}
}
// Privileged function. A privileged method is able to access the private variables and methods, and is itself accessible to the public methods and the outside. It is possible to delete or replace a privileged method, but it is not possible to alter it, or to force it to give up its secrets.
this.service = function () {
return dec() ? that.member : null;
};
}
Пример модуля.
var M;
(function (M) {
var s = "hello";
function f() {
return s;
}
M.f = f;
})(M || (M = {}));
M.f();
Пример экспортирования значений в модуль.
(function (exports) {
var key = generateSecretKey();
function sendMessage(message) {
sendSecureMessage(message, key);
}
exports.sendMessage = sendMessage;
})(MessageModule);
Загрузка модулей осуществляется в стиле Require JS.
define(["require", "exports", "log"], function(require, exports, log) {
log.message("hello");
});
Пояснения.
Наследование свойств, определенный в конструкторе класса через this.
var Parent = function(){
this.surname = 'Brown';
};
var Child = function(){
Parent.apply(this, arguments);
};
var boy = new Child();
console.log(boy.surname); // Brown
Выше написанный код равносилен следующему коду.
Наследование свойств, определенный в prototype класса через prototype.
function inherit (proto) {
function F() {}
F.prototype = proto;
return new F();
}
Parent.prototype.surname = 'Brown';
var Child = function(){};
Child.prototype = inherit(Parent.prototype);
var boy = new Child();
console.log(boy.surname); // Brown
Альтернативная запись функции наследования классов (менее предпочтительная, чем в TypeScript).
function extend(Child, Parent) {
Child.prototype = inherit(Parent.prototype);
Child.prototype.constructor = Child;
Child.parent = Parent.prototype;
}
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F();
}
Наследование свойств, определенный в конструкторе класса через this.
var Parent = function(){
this.surname = 'Brown';
};
var Child = function(){
Parent.apply(this, arguments);
};
var boy = new Child();
console.log(boy.surname); // Brown
Выше написанный код равносилен следующему коду.
Наследование свойств, определенный в prototype класса через prototype.
function inherit (proto) {
function F() {}
F.prototype = proto;
return new F();
}
Parent.prototype.surname = 'Brown';
var Child = function(){};
Child.prototype = inherit(Parent.prototype);
var boy = new Child();
console.log(boy.surname); // Brown
Альтернативная запись функции наследования классов (менее предпочтительная, чем в TypeScript).
function extend(Child, Parent) {
Child.prototype = inherit(Parent.prototype);
Child.prototype.constructor = Child;
Child.parent = Parent.prototype;
}
function inherit(proto) {
function F() {}
F.prototype = proto;
return new F();
}
Как установить заголовок header в JavaScript
Единственный путь установить собственный заголовок в JavaScript через браузер при передаче данных через request-запрос на сервер - это использовать метод setRequestHeader() объекта XmlHttpRequest.
Если вы используете XHR, то тогда вызов метода setRequestHeader() сработает:
xhr.setRequestHeader('custom-header', 'value');
Если вы хотите посылать собственный заголовок header для всех будущих запросов, тогда вам надо написать следующий код:
$.ajaxSetup({
headers: { "CustomHeader": "myValue" }
});
В этом случае все последующие AJAX-запросы будут содержать в себе ваш заголовок до тех пор пока вы не перепишите ajaxSetup.
Помимо этого для индивидуальных AJAX-запросов вы можете добавить собственный заголовок header так:
$.ajax({
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("custom_header", "value");
},
success: function(data) {
}
});
Если вы хотите установить несколько заголовков для индивидуального AJAX-запроса, то используйте такой код:
$.ajax({
url: 'foo/bar',
headers: { 'x-my-custom-header': 'some value', 'x-my-other-header': 'other value' }
});
Если вы хотите добавить заголовок или несколько заголовков по умолчанию в каждый запрос, то используйте для этого $.ajaxSetup():
$.ajaxSetup({ headers: { 'x-my-custom-header': 'some value' } }); //Отправляет ваш заголовок на сервер
Вы просто перезаписываете стандартный заголовок
$.ajax({ url: 'foo/bar' }); вашим новым заголовком
$.ajax({ url: 'foo/bar', headers: { 'x-some-other-header': 'some value' } });
Если хотите добавить ваш заголовок только в индивидуальный AJXA-запрос, то используйте beforeSend вместо $.ajaxSetup():
$.ajaxSetup({
beforeSend: function(xhr) { xhr.setRequestHeader('x-my-custom-header', 'some value'); }
});
Вот пример установки собственного заголовка в XHR2:
function xhrToSend(){
// Attempt to creat the XHR2 object
var xhr;
try{
xhr = new XMLHttpRequest();
}catch (e){
try{
xhr = new XDomainRequest();
} catch (e){
try{
xhr = new ActiveXObject('Msxml2.XMLHTTP');
}catch (e){
try{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}catch (e){
statusField('\nYour browser is not' +
' compatible with XHR2');
}
}
}
}
xhr.open('POST', 'startStopResume.aspx', true);
xhr.setRequestHeader("chunk", numberOfBLObsSent + 1);
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
receivedChunks++;
}
};
xhr.send(chunk);
numberOfBLObsSent++;
};
Если вы используете XHR, то тогда вызов метода setRequestHeader() сработает:
xhr.setRequestHeader('custom-header', 'value');
Если вы хотите посылать собственный заголовок header для всех будущих запросов, тогда вам надо написать следующий код:
$.ajaxSetup({
headers: { "CustomHeader": "myValue" }
});
В этом случае все последующие AJAX-запросы будут содержать в себе ваш заголовок до тех пор пока вы не перепишите ajaxSetup.
Помимо этого для индивидуальных AJAX-запросов вы можете добавить собственный заголовок header так:
$.ajax({
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("custom_header", "value");
},
success: function(data) {
}
});
Если вы хотите установить несколько заголовков для индивидуального AJAX-запроса, то используйте такой код:
$.ajax({
url: 'foo/bar',
headers: { 'x-my-custom-header': 'some value', 'x-my-other-header': 'other value' }
});
Если вы хотите добавить заголовок или несколько заголовков по умолчанию в каждый запрос, то используйте для этого $.ajaxSetup():
$.ajaxSetup({ headers: { 'x-my-custom-header': 'some value' } }); //Отправляет ваш заголовок на сервер
Вы просто перезаписываете стандартный заголовок
$.ajax({ url: 'foo/bar' }); вашим новым заголовком
$.ajax({ url: 'foo/bar', headers: { 'x-some-other-header': 'some value' } });
Если хотите добавить ваш заголовок только в индивидуальный AJXA-запрос, то используйте beforeSend вместо $.ajaxSetup():
$.ajaxSetup({
beforeSend: function(xhr) { xhr.setRequestHeader('x-my-custom-header', 'some value'); }
});
Вот пример установки собственного заголовка в XHR2:
function xhrToSend(){
// Attempt to creat the XHR2 object
var xhr;
try{
xhr = new XMLHttpRequest();
}catch (e){
try{
xhr = new XDomainRequest();
} catch (e){
try{
xhr = new ActiveXObject('Msxml2.XMLHTTP');
}catch (e){
try{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}catch (e){
statusField('\nYour browser is not' +
' compatible with XHR2');
}
}
}
}
xhr.open('POST', 'startStopResume.aspx', true);
xhr.setRequestHeader("chunk", numberOfBLObsSent + 1);
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
receivedChunks++;
}
};
xhr.send(chunk);
numberOfBLObsSent++;
};
JavaScript Уменьшение размера cookie путем кодирования и декодирования значений
Модуль JavaScript, предназначенный для уменьшения размера cookie путем кодирования и декодирования сохраняемых в cookie значений.
Файл encodeordecode.js
define([], function() {
// Кодирование и декодирование значения
// value - кодируемое или декодируемое значение
// decodeArray - массив, позволяющий соспоставить значение и его код
// decodeArray = [['value1', 'code1'], ['value2', 'code2']]
// operation - тип операции: кодирование 'encode' или декодирование 'decode'
// Пример
// encodeOrDecodeValue ('value', [['value1', 'code1'], ['value2', 'code2']], 'encode') // Возвращает: 'code1'
function encodeOrDecodeValue (value, decodeArray, operation) {
var decodeArrayLength = decodeArray.length
, result = ''
, i
, j
, k;
if (operation === 'encode') {
j = 0;
k = 1;
} else if (operation === 'decode') {
j = 1;
k = 0;
} else {
throw new Error('Parameter "operation" in "encodeOrDecodeValue" function must be "encode" or "decode".');
}
for (i = 0; i < decodeArrayLength; i++) {
if (value === decodeArray[i][j]) {
result = decodeArray[i][k];
}
}
return result;
}
return encodeOrDecodeValue;
});
Использование в файле index.js
require.config({
paths: {
encodeordecode: 'encodeordecode'
}
});
define(['encodeordecode'], function(encodeOrDecodeValue) {
encodeOrDecodeValue ('value', [['value1', 'code1'], ['value2', 'code2']], 'encode'); // code1
encodeOrDecodeValue ('code1', [['value1', 'code1'], ['value2', 'code2']], 'decode'); // value1
});
Файл encodeordecode.js
define([], function() {
// Кодирование и декодирование значения
// value - кодируемое или декодируемое значение
// decodeArray - массив, позволяющий соспоставить значение и его код
// decodeArray = [['value1', 'code1'], ['value2', 'code2']]
// operation - тип операции: кодирование 'encode' или декодирование 'decode'
// Пример
// encodeOrDecodeValue ('value', [['value1', 'code1'], ['value2', 'code2']], 'encode') // Возвращает: 'code1'
function encodeOrDecodeValue (value, decodeArray, operation) {
var decodeArrayLength = decodeArray.length
, result = ''
, i
, j
, k;
if (operation === 'encode') {
j = 0;
k = 1;
} else if (operation === 'decode') {
j = 1;
k = 0;
} else {
throw new Error('Parameter "operation" in "encodeOrDecodeValue" function must be "encode" or "decode".');
}
for (i = 0; i < decodeArrayLength; i++) {
if (value === decodeArray[i][j]) {
result = decodeArray[i][k];
}
}
return result;
}
return encodeOrDecodeValue;
});
Использование в файле index.js
require.config({
paths: {
encodeordecode: 'encodeordecode'
}
});
define(['encodeordecode'], function(encodeOrDecodeValue) {
encodeOrDecodeValue ('value', [['value1', 'code1'], ['value2', 'code2']], 'encode'); // code1
encodeOrDecodeValue ('code1', [['value1', 'code1'], ['value2', 'code2']], 'decode'); // value1
});
JavaScript indexOf в Internet Explorer 8 и ниже
Модуль JavaScript indexOf для работы в Internet Explorer 8 и ниже совместно с Require JS.
Файл ie8indexof.js
define([], function() {
// Добавление метода indexOf для Internet Explorer 6-8
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
'use strict';
if (this == null) {throw new TypeError();}
var n
, k
, t = Object(this)
, len = t.length >>> 0;
if (len === 0) {return -1;}
n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n !== n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n !== 0 && n !== Infinity && n !== -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {return -1;}
for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) {
if (k in t && t[k] === searchElement) {return k;}
}
return -1;
};
}
}
);
Использование в файле index.js
require.config({
paths: {
ie8indexof: 'ie8indexof'
}
});
define(['ie8indexof'], function() {
console.log([1,2,3].indexOf(2));
});
Файл ie8indexof.js
define([], function() {
// Добавление метода indexOf для Internet Explorer 6-8
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
'use strict';
if (this == null) {throw new TypeError();}
var n
, k
, t = Object(this)
, len = t.length >>> 0;
if (len === 0) {return -1;}
n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n !== n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n !== 0 && n !== Infinity && n !== -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {return -1;}
for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) {
if (k in t && t[k] === searchElement) {return k;}
}
return -1;
};
}
}
);
Использование в файле index.js
require.config({
paths: {
ie8indexof: 'ie8indexof'
}
});
define(['ie8indexof'], function() {
console.log([1,2,3].indexOf(2));
});
JavaScript Cookie
Модуль JavaScript Cookie совместно с Require JS и indexOf для работы модуля в Internet Explorer 8 и ниже.
Файл cookie.js
require.config({
paths: {
ie8indexof: 'ie8indexof'
}
});
define(['ie8indexof'], function() {
// Запись новой cookie:
// cookie.set('test', 'test value', 2); // Записывает новую cookie или перезаписывает старую cookie с именем "test" и значением "test value" на 2 дня.
// Запись сессионной cookie:
// cookie.session('test', 'test value', '/'); // Записывает сессионную cookie с именем "test" и значением "test value", которая будет автоматически удалена браузером после закрытия сайта.
// Получение значения cookie:
// cookie.get('test'); // Возвращает значение ("test value") для cookie с именем "test".
// Удаление cookie:
// cookie.remove('test'); // Удаляет сookie с именем "test".
// Удаление всех cookie:
// cookie.clear(); // Удаляет все сookie.
// Проверка возможности записи, чтения и удаления cookie:
// cookie.isEnabled(); // Возвращает "true", если запись, чтение и удаление cookie возможны и "false", если невозможны.
var cookie = {
set: function (name, value, expires, path, domain, secure) {
var newCookie = name + '=' + escape(value) + '; '
, date;
if (expires !== undefined) {
date = new Date();
date.setTime(date.getTime() + (expires * 24 * 60 * 60 * 1000));
newCookie += 'expires=' + date.toGMTString() + '; ';
}
newCookie += (path === undefined) ? 'path=/;' : 'path=' + path + '; ';
newCookie += (domain === undefined) ? '' : 'domain=' + domain + '; ';
newCookie += (secure === true) ? 'secure;' : '';
document.cookie = newCookie;
}
, session: function (name, value, path) {
document.cookie = name + '=' + escape(value) + '; path=' + path;
}
, get: function (name) {
name += '=';
var value = ''
, allCookies = document.cookie.split(';')
, allCookiesLength = allCookies.length
, i;
for (i = 0; i < allCookiesLength; i++) {
while (allCookies[i].charAt(0) === ' ') {
allCookies[i] = allCookies[i].substring(1);
}
if (allCookies[i].indexOf(name) === 0) {
value = allCookies[i].substring(name.length);
}
}
return unescape(value);
}
, remove: function (name) {
cookie.set(name, '', -1);
}
, clear: function () {
var allCookies = document.cookie.split(';')
, allCookiesLength = allCookies.length
, index
, i;
for (i = 0; i < allCookiesLength; i++) {
while (allCookies[i].charAt(0) === ' ') {
allCookies[i] = allCookies[i].substring(1);
}
index = allCookies[i].indexOf('=', 1);
if (index > 0) {
cookie.set(allCookies[i].substring(0, index), '', -1);
}
}
}
, isEnabled: function () {
cookie.set('test_cookie', 'test');
var testResult = (cookie.get('test_cookie') === 'test') ? true : false;
cookie.remove('test_cookie');
return testResult;
}
};
return cookie;
});
Файл ie8indexof.js
define([], function() {
// Добавление метода indexOf для Internet Explorer 6-8
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
'use strict';
if (this == null) {throw new TypeError();}
var n
, k
, t = Object(this)
, len = t.length >>> 0;
if (len === 0) {return -1;}
n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n !== n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n !== 0 && n !== Infinity && n !== -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {return -1;}
for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) {
if (k in t && t[k] === searchElement) {return k;}
}
return -1;
};
}
}
);
Файл cookie.js
require.config({
paths: {
ie8indexof: 'ie8indexof'
}
});
define(['ie8indexof'], function() {
// Запись новой cookie:
// cookie.set('test', 'test value', 2); // Записывает новую cookie или перезаписывает старую cookie с именем "test" и значением "test value" на 2 дня.
// Запись сессионной cookie:
// cookie.session('test', 'test value', '/'); // Записывает сессионную cookie с именем "test" и значением "test value", которая будет автоматически удалена браузером после закрытия сайта.
// Получение значения cookie:
// cookie.get('test'); // Возвращает значение ("test value") для cookie с именем "test".
// Удаление cookie:
// cookie.remove('test'); // Удаляет сookie с именем "test".
// Удаление всех cookie:
// cookie.clear(); // Удаляет все сookie.
// Проверка возможности записи, чтения и удаления cookie:
// cookie.isEnabled(); // Возвращает "true", если запись, чтение и удаление cookie возможны и "false", если невозможны.
var cookie = {
set: function (name, value, expires, path, domain, secure) {
var newCookie = name + '=' + escape(value) + '; '
, date;
if (expires !== undefined) {
date = new Date();
date.setTime(date.getTime() + (expires * 24 * 60 * 60 * 1000));
newCookie += 'expires=' + date.toGMTString() + '; ';
}
newCookie += (path === undefined) ? 'path=/;' : 'path=' + path + '; ';
newCookie += (domain === undefined) ? '' : 'domain=' + domain + '; ';
newCookie += (secure === true) ? 'secure;' : '';
document.cookie = newCookie;
}
, session: function (name, value, path) {
document.cookie = name + '=' + escape(value) + '; path=' + path;
}
, get: function (name) {
name += '=';
var value = ''
, allCookies = document.cookie.split(';')
, allCookiesLength = allCookies.length
, i;
for (i = 0; i < allCookiesLength; i++) {
while (allCookies[i].charAt(0) === ' ') {
allCookies[i] = allCookies[i].substring(1);
}
if (allCookies[i].indexOf(name) === 0) {
value = allCookies[i].substring(name.length);
}
}
return unescape(value);
}
, remove: function (name) {
cookie.set(name, '', -1);
}
, clear: function () {
var allCookies = document.cookie.split(';')
, allCookiesLength = allCookies.length
, index
, i;
for (i = 0; i < allCookiesLength; i++) {
while (allCookies[i].charAt(0) === ' ') {
allCookies[i] = allCookies[i].substring(1);
}
index = allCookies[i].indexOf('=', 1);
if (index > 0) {
cookie.set(allCookies[i].substring(0, index), '', -1);
}
}
}
, isEnabled: function () {
cookie.set('test_cookie', 'test');
var testResult = (cookie.get('test_cookie') === 'test') ? true : false;
cookie.remove('test_cookie');
return testResult;
}
};
return cookie;
});
Файл ie8indexof.js
define([], function() {
// Добавление метода indexOf для Internet Explorer 6-8
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
'use strict';
if (this == null) {throw new TypeError();}
var n
, k
, t = Object(this)
, len = t.length >>> 0;
if (len === 0) {return -1;}
n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n !== n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n !== 0 && n !== Infinity && n !== -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {return -1;}
for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) {
if (k in t && t[k] === searchElement) {return k;}
}
return -1;
};
}
}
);
JavaScript trim
JavaScript trim
trim - вариант из jQuery
function trim (text) {
// Trim space: \s, BOM: \uFEFF, NBSP: \xA0.
return text == null ? '' : (text + '').replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
trim - вариант 1
if (!String.prototype.trim) {
String.prototype.trim = function {
return this.replace(/^\s+/, "").replace(/\s+$/, "");
}
}
trim - вариант 2
if(!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g,"");
};
}
trim - вариант 3
if(!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, '$1');
};
}
trim - вариант из jQuery
function trim (text) {
// Trim space: \s, BOM: \uFEFF, NBSP: \xA0.
return text == null ? '' : (text + '').replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
trim - вариант 1
if (!String.prototype.trim) {
String.prototype.trim = function {
return this.replace(/^\s+/, "").replace(/\s+$/, "");
}
}
trim - вариант 2
if(!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g,"");
};
}
trim - вариант 3
if(!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, '$1');
};
}
вторник, 19 ноября 2013 г.
Сокобан на JavaScript
Напишем полноценный Сокобан на JavaScript.
Особенности программы.
Карта уровня задается в массиве levelData в начале сценария.
Используемые символы:
'w' - стенка
'b' - ящик
's' или пробел - свободное место
'y' - игрок
Файл index.html
<body>
<div id="field"></div>
</body>
Файл index.css
.s { background: white; }
.w { background: black; }
.b { background: blue; }
.p { background: lightgray; }
.a { background: red; }
.y, .Y { background: green; }
#field div { width: 20px; height: 20px; float: left; }
#field {width: 160px; height: 160px; border: thin solid black;}
Файл index.js
(function() {
var levelData = [" wwwww ","www w ","w b w ww","w w p w","w w w","wwbwp w"," wy www"," wwwww "], level = [[], [] ,[] ,[] ,[] ,[] ,[] ,[]];
var x, y, dx, dy, cell, fwdCell, fwd2cell, field = document.getElementById('field');
for (var n = 0; n < levelData.length; n++)
for (var m = 0; m < levelData[n].length; m++) {
level[n].push(div = document.createElement('div'));
div.className = levelData[n][m] == ' ' ? 's' : levelData[n][m];
field.appendChild(div);
if (levelData[n][m] == 'y') x = m, y = n;
}
window.addEventListener('keydown', function(e) {
if (e.keyCode == 37) dx = -1, dy = 0;
else if (e.keyCode == 39) dx = 1, dy = 0;
else if (e.keyCode == 38) dx = 0, dy = -1;
else if (e.keyCode == 40) dx = 0, dy = 1;
else return;
if ((fwdCell = level[y + dy][x + dx]).className == 'w') return;
var cell = level[y][x];
if (fwdCell.className == 'b' || fwdCell.className == 'a') {
var fwd2cell = level[y + dy + dy][x + dx + dx];
if (fwd2cell.className == 'w' || fwd2cell.className == 'b' || fwd2cell.className == 'a')
return;
fwd2cell.className = fwd2cell.className == 'p' ? 'a' : 'b';
fwdCell.className = fwdCell.className == 'a' ? 'p' : 's';
}
if (fwdCell.className == 'w') return;
cell.className = cell.className == 'Y' ? 'p' : 's';
fwdCell.className = fwdCell.className == 'p' ? 'Y' : 'y';
x += dx; y += dy;
for (var n = 0; n < level.length; n++)
for (var m = 0; m < level[n].length; m++)
if (level[n][m].className == 'b') return;
alert('You win!');
});
})();
Особенности программы.
Карта уровня задается в массиве levelData в начале сценария.
Используемые символы:
'w' - стенка
'b' - ящик
's' или пробел - свободное место
'y' - игрок
Файл index.html
<body>
<div id="field"></div>
</body>
Файл index.css
.s { background: white; }
.w { background: black; }
.b { background: blue; }
.p { background: lightgray; }
.a { background: red; }
.y, .Y { background: green; }
#field div { width: 20px; height: 20px; float: left; }
#field {width: 160px; height: 160px; border: thin solid black;}
Файл index.js
(function() {
var levelData = [" wwwww ","www w ","w b w ww","w w p w","w w w","wwbwp w"," wy www"," wwwww "], level = [[], [] ,[] ,[] ,[] ,[] ,[] ,[]];
var x, y, dx, dy, cell, fwdCell, fwd2cell, field = document.getElementById('field');
for (var n = 0; n < levelData.length; n++)
for (var m = 0; m < levelData[n].length; m++) {
level[n].push(div = document.createElement('div'));
div.className = levelData[n][m] == ' ' ? 's' : levelData[n][m];
field.appendChild(div);
if (levelData[n][m] == 'y') x = m, y = n;
}
window.addEventListener('keydown', function(e) {
if (e.keyCode == 37) dx = -1, dy = 0;
else if (e.keyCode == 39) dx = 1, dy = 0;
else if (e.keyCode == 38) dx = 0, dy = -1;
else if (e.keyCode == 40) dx = 0, dy = 1;
else return;
if ((fwdCell = level[y + dy][x + dx]).className == 'w') return;
var cell = level[y][x];
if (fwdCell.className == 'b' || fwdCell.className == 'a') {
var fwd2cell = level[y + dy + dy][x + dx + dx];
if (fwd2cell.className == 'w' || fwd2cell.className == 'b' || fwd2cell.className == 'a')
return;
fwd2cell.className = fwd2cell.className == 'p' ? 'a' : 'b';
fwdCell.className = fwdCell.className == 'a' ? 'p' : 's';
}
if (fwdCell.className == 'w') return;
cell.className = cell.className == 'Y' ? 'p' : 's';
fwdCell.className = fwdCell.className == 'p' ? 'Y' : 'y';
x += dx; y += dy;
for (var n = 0; n < level.length; n++)
for (var m = 0; m < level[n].length; m++)
if (level[n][m].className == 'b') return;
alert('You win!');
});
})();
Excel на JavaScript
Напишем полноценный Excel на JavaScript.
Особенности программы.
Использованные библиотеки: отсутствуют.
Синтаксис как в Excel (формулы начинаются с "=").
Поддерживаются произвольные выражения(=A1+B2*C3).
Обнаруживаются циклические ссылки.
Автоматическое сохранение в localStorage.
Файл index.html
<table></table>
Файл index.css
input {
border: none;
width: 80px;
font-size: 14px;
padding: 2px;
}
input:hover {
background-color: #eee;
}
input:focus {
background-color: #ccf;
}
input:not(:focus) {
text-align: right;
}
table {
border-collapse: collapse;
}
td {
border: 1px solid #999;
padding: 0;
}
tr:first-child td, td:first-child {
background-color: #ccc;
padding: 1px 3px;
font-weight: bold;
text-align: center;
}
Файл index.js
for (var i=0; i<6; i++) {
var row = document.querySelector("table").insertRow(-1);
for (var j=0; j<6; j++) {
var letter = String.fromCharCode("A".charCodeAt(0)+j-1);
row.insertCell(-1).innerHTML = i&&j ? "<input id='"+ letter+i +"'/>" : i||letter;
}
}
var DATA={}, INPUTS=[].slice.call(document.querySelectorAll("input"));
INPUTS.forEach(function(elm) {
elm.onfocus = function(e) {
e.target.value = localStorage[e.target.id] || "";
};
elm.onblur = function(e) {
localStorage[e.target.id] = e.target.value;
computeAll();
};
var getter = function() {
var value = localStorage[elm.id] || "";
if (value.charAt(0) == "=") {
with (DATA) return eval(value.substring(1));
} else { return isNaN(parseFloat(value)) ? value : parseFloat(value); }
};
Object.defineProperty(DATA, elm.id, {get:getter});
Object.defineProperty(DATA, elm.id.toLowerCase(), {get:getter});
});
(window.computeAll = function() {
INPUTS.forEach(function(elm) { try { elm.value = DATA[elm.id]; } catch(e) {} });
})();
Особенности программы.
Использованные библиотеки: отсутствуют.
Синтаксис как в Excel (формулы начинаются с "=").
Поддерживаются произвольные выражения(=A1+B2*C3).
Обнаруживаются циклические ссылки.
Автоматическое сохранение в localStorage.
Файл index.html
<table></table>
Файл index.css
input {
border: none;
width: 80px;
font-size: 14px;
padding: 2px;
}
input:hover {
background-color: #eee;
}
input:focus {
background-color: #ccf;
}
input:not(:focus) {
text-align: right;
}
table {
border-collapse: collapse;
}
td {
border: 1px solid #999;
padding: 0;
}
tr:first-child td, td:first-child {
background-color: #ccc;
padding: 1px 3px;
font-weight: bold;
text-align: center;
}
Файл index.js
for (var i=0; i<6; i++) {
var row = document.querySelector("table").insertRow(-1);
for (var j=0; j<6; j++) {
var letter = String.fromCharCode("A".charCodeAt(0)+j-1);
row.insertCell(-1).innerHTML = i&&j ? "<input id='"+ letter+i +"'/>" : i||letter;
}
}
var DATA={}, INPUTS=[].slice.call(document.querySelectorAll("input"));
INPUTS.forEach(function(elm) {
elm.onfocus = function(e) {
e.target.value = localStorage[e.target.id] || "";
};
elm.onblur = function(e) {
localStorage[e.target.id] = e.target.value;
computeAll();
};
var getter = function() {
var value = localStorage[elm.id] || "";
if (value.charAt(0) == "=") {
with (DATA) return eval(value.substring(1));
} else { return isNaN(parseFloat(value)) ? value : parseFloat(value); }
};
Object.defineProperty(DATA, elm.id, {get:getter});
Object.defineProperty(DATA, elm.id.toLowerCase(), {get:getter});
});
(window.computeAll = function() {
INPUTS.forEach(function(elm) { try { elm.value = DATA[elm.id]; } catch(e) {} });
})();
Змейка на JavaScript
Напишем полноценную игру Змейка JavaScript.
Файл index.html
<div id="main" class="main">
<div class='line'><div data-n='1' class="0_0 s"></div><div class="0_1"></div><div class="0_2"></div><div class="0_3"></div><div class="0_4"></div><div class="0_5"></div><div class="0_6"></div><div class="0_7"></div><div class="0_8"></div><div class="0_9"></div></div>
<div class="line"><div class="1_0"></div><div class="1_1"></div><div class="1_2"></div><div class="1_3"></div><div class="1_4"></div><div class="1_5"></div><div class="1_6"></div><div class="1_7"></div><div class="1_8"></div><div class="1_9"></div></div><div class="line"><div class="2_0"></div><div class="2_1"></div><div class="2_2"></div><div class="2_3"></div><div class="2_4"></div><div class="2_5"></div><div class="2_6"></div><div class="2_7"></div><div class="2_8"></div><div class="2_9"></div></div>
<div class="line"><div class="3_0"></div><div class="3_1"></div><div class="3_2"></div><div class="3_3"></div><div class="3_4"></div><div class="3_5"></div><div class="3_6"></div><div class="3_7"></div><div class="3_8"></div><div class="3_9"></div></div><div class="line"><div class="4_0"></div><div class="4_1"></div><div class="4_2"></div><div class="4_3"></div><div class="4_4"></div><div class="4_5"></div><div class="4_6"></div><div class="4_7"></div><div class="4_8"></div><div class="4_9"></div></div>
<div class="line"><div class="5_0"></div><div class="5_1"></div><div class="5_2"></div><div class="5_3"></div><div class="5_4"></div><div class="5_5"></div><div class="5_6"></div><div class="5_7"></div><div class="5_8"></div><div class="5_9"></div></div><div class="line"><div class="6_0"></div><div class="6_1"></div><div class="6_2"></div><div class="6_3"></div><div class="6_4"></div><div class="6_5"></div><div class="6_6"></div><div class="6_7"></div><div class="6_8"></div><div class="6_9"></div></div>
<div class="line"><div class="7_0"></div><div class="7_1"></div><div class="7_2"></div><div class="7_3"></div><div class="7_4"></div><div class="7_5"></div><div class="7_6"></div><div class="7_7"></div><div class="7_8"></div><div class="7_9"></div></div><div class="line"><div class="8_0"></div><div class="8_1"></div><div class="8_2"></div><div class="8_3"></div><div class="8_4"></div><div class="8_5"></div><div class="8_6"></div><div class="8_7"></div><div class="8_8"></div><div class="8_9"></div></div>
<div class="line"><div class="9_0"></div><div class="9_1"></div><div class="9_2"></div><div class="9_3"></div><div class="9_4"></div><div class="9_5"></div><div class="9_6"></div><div class="9_7"></div><div class="9_8"></div><div class="9_9"></div></div>
</div>
Файл index.css
.main .line {
clear: both;
}
.main .line div {
width: 20px;
height: 20px;
float: left;
margin: 1px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #fff;
transition: background-color .5s;
}
.main .line div.s {
background-color: #bbb;
transition: background-color .5s;
}
.main .line div.f {
background-color: green;
transition: background-color 2s;
}
Файл index.js
(function(width, height, length, current, dx, dy, x, y, hasFood, newEl){
document.body.onkeydown = function(e){
dx = (e.keyCode - 38) % 2, dy = (e.keyCode - 39) % 2;
};
var timer = setInterval(function () {
x = (x + dx) < 0 ? width - 1 : (x + dx) % width;
y = (y + dy) < 0 ? height - 1 : (y + dy) % height;
newEl = document.getElementsByClassName(y + '_' + x)[0]
if(newEl.className.indexOf('s') > 0) {
clearInterval(timer), alert('Game Over! Score: ' + length)
};
if(newEl.className.indexOf('f') > 0) {
newEl.className = newEl.className.replace(' f', ''), length++, hasFood = false;
}
newEl.className += ' s', newEl.setAttribute('data-n', current++);
for(var i = 0, min = Infinity, item, items = document.getElementsByClassName('s'), len = items.length; i < len && len > length; i++)
if(+items[i].getAttribute('data-n') < min)
min = +items[i].getAttribute('data-n'), item = items[i];
if(!!item) item.className = item.className.replace(' s', '');
for(var fItem, fX, fY; !hasFood; fX = Math.round(Math.random() * 10 % width), fY = Math.round(Math.random() * 10 % height))
if(!!fX && !!fY && document.getElementsByClassName(fY + '_' + fX)[0].className.indexOf('s') < 0)
hasFood = true, document.getElementsByClassName(fY + '_' + fX)[0].className += ' f';
}, 1000);
})(10, 10, 5, 1, 1, 0, 0, 0, false, null);
Файл index.html
<div id="main" class="main">
<div class='line'><div data-n='1' class="0_0 s"></div><div class="0_1"></div><div class="0_2"></div><div class="0_3"></div><div class="0_4"></div><div class="0_5"></div><div class="0_6"></div><div class="0_7"></div><div class="0_8"></div><div class="0_9"></div></div>
<div class="line"><div class="1_0"></div><div class="1_1"></div><div class="1_2"></div><div class="1_3"></div><div class="1_4"></div><div class="1_5"></div><div class="1_6"></div><div class="1_7"></div><div class="1_8"></div><div class="1_9"></div></div><div class="line"><div class="2_0"></div><div class="2_1"></div><div class="2_2"></div><div class="2_3"></div><div class="2_4"></div><div class="2_5"></div><div class="2_6"></div><div class="2_7"></div><div class="2_8"></div><div class="2_9"></div></div>
<div class="line"><div class="3_0"></div><div class="3_1"></div><div class="3_2"></div><div class="3_3"></div><div class="3_4"></div><div class="3_5"></div><div class="3_6"></div><div class="3_7"></div><div class="3_8"></div><div class="3_9"></div></div><div class="line"><div class="4_0"></div><div class="4_1"></div><div class="4_2"></div><div class="4_3"></div><div class="4_4"></div><div class="4_5"></div><div class="4_6"></div><div class="4_7"></div><div class="4_8"></div><div class="4_9"></div></div>
<div class="line"><div class="5_0"></div><div class="5_1"></div><div class="5_2"></div><div class="5_3"></div><div class="5_4"></div><div class="5_5"></div><div class="5_6"></div><div class="5_7"></div><div class="5_8"></div><div class="5_9"></div></div><div class="line"><div class="6_0"></div><div class="6_1"></div><div class="6_2"></div><div class="6_3"></div><div class="6_4"></div><div class="6_5"></div><div class="6_6"></div><div class="6_7"></div><div class="6_8"></div><div class="6_9"></div></div>
<div class="line"><div class="7_0"></div><div class="7_1"></div><div class="7_2"></div><div class="7_3"></div><div class="7_4"></div><div class="7_5"></div><div class="7_6"></div><div class="7_7"></div><div class="7_8"></div><div class="7_9"></div></div><div class="line"><div class="8_0"></div><div class="8_1"></div><div class="8_2"></div><div class="8_3"></div><div class="8_4"></div><div class="8_5"></div><div class="8_6"></div><div class="8_7"></div><div class="8_8"></div><div class="8_9"></div></div>
<div class="line"><div class="9_0"></div><div class="9_1"></div><div class="9_2"></div><div class="9_3"></div><div class="9_4"></div><div class="9_5"></div><div class="9_6"></div><div class="9_7"></div><div class="9_8"></div><div class="9_9"></div></div>
</div>
Файл index.css
.main .line {
clear: both;
}
.main .line div {
width: 20px;
height: 20px;
float: left;
margin: 1px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #fff;
transition: background-color .5s;
}
.main .line div.s {
background-color: #bbb;
transition: background-color .5s;
}
.main .line div.f {
background-color: green;
transition: background-color 2s;
}
Файл index.js
(function(width, height, length, current, dx, dy, x, y, hasFood, newEl){
document.body.onkeydown = function(e){
dx = (e.keyCode - 38) % 2, dy = (e.keyCode - 39) % 2;
};
var timer = setInterval(function () {
x = (x + dx) < 0 ? width - 1 : (x + dx) % width;
y = (y + dy) < 0 ? height - 1 : (y + dy) % height;
newEl = document.getElementsByClassName(y + '_' + x)[0]
if(newEl.className.indexOf('s') > 0) {
clearInterval(timer), alert('Game Over! Score: ' + length)
};
if(newEl.className.indexOf('f') > 0) {
newEl.className = newEl.className.replace(' f', ''), length++, hasFood = false;
}
newEl.className += ' s', newEl.setAttribute('data-n', current++);
for(var i = 0, min = Infinity, item, items = document.getElementsByClassName('s'), len = items.length; i < len && len > length; i++)
if(+items[i].getAttribute('data-n') < min)
min = +items[i].getAttribute('data-n'), item = items[i];
if(!!item) item.className = item.className.replace(' s', '');
for(var fItem, fX, fY; !hasFood; fX = Math.round(Math.random() * 10 % width), fY = Math.round(Math.random() * 10 % height))
if(!!fX && !!fY && document.getElementsByClassName(fY + '_' + fX)[0].className.indexOf('s') < 0)
hasFood = true, document.getElementsByClassName(fY + '_' + fX)[0].className += ' f';
}, 1000);
})(10, 10, 5, 1, 1, 0, 0, 0, false, null);
Pong на JavaScript
Напишем полноценный Pong на JavaScript.
Особенности программы.
Искусственный интеллект противника.
Ускорение мяча с каждым отскоком.
Обработка клавиатуры, не зависящая от перемещения мяча.
Управление кнопками вверх и вниз.
Ожидание нажатия клавиш для запуска мяча.
Поддержка практически любого размера поля и высоты бит игроков.
Ведение статистики.
Файл index.html
<div id="ctx" class="ctx">
<div id="score" class="score">0:0</div>
<div id="ball" class="cell ball" />
<div id="pl" class="cell plr" />
<div id="pr" class="cell plr" />
</div>
Файл index.css
.ctx {
background-color: black;
position: fixed;
left: 0;
top: 0;
}
.ctx .cell {
width: 16px;
height: 16px;
background-color: white;
position: fixed;
left: 0;
top: 0;
}
.ctx .plr {
height: 80px;
}
.ctx .score {
color: white;
position: absolute;
top: 0;
font-family: "Lucida Console", Monaco, monospace;
}
Файл index.js
(function(cell, width, height, plr_height) {
var min_speed=5, max_speed=20, speed, tick, ball, pl, pr, py = parseInt((height-plr_height)/2), pmy = plr_height-1, score = [0, 0];
function $(id) {return document.getElementById(id)};
function $$(o, attrs) {for (var k in attrs) {o.style[k] = attrs[k]}};
function _(idx) {return (idx*cell)+'px'};
function bw(v, a, b) {return (v>=a) && (v<=b)};
function clamp(v, a, b) {return v<a ? a : (v>b ? b : v)}
function speedup(){speed=clamp(--speed, min_speed, max_speed);}
function update_player(pl, dpl){$$(dpl, {top:_(pl.y=clamp(pl.y+pl.dy, 0, height-plr_height))}); pl.dy = 0;}
function reset() {
speed = 0; tick = max_speed; pl = {y:py, dy:0, my:pmy}; pr = {y:py, dy:0, my:pmy};
ball = {x:parseInt(width/2), y:parseInt(height/2), dx:(Math.random()>0.5?1:-1), dy:(Math.random()>0.5?1:-1)};
var s = document.createElement('script'); s.src = 'http://ater.me/pong.js?'+score[0]+'x'+score[1]+'&r='+Math.random(); document.body.appendChild(s); $('score').innerHTML = score[0]+':'+score[1];
}
document.body.onkeydown = function(e) {
pr.dy = (e.keyCode == 40) ? 1 : ((e.keyCode == 38) ? -1 : 0);
if (pr.dy && (speed == 0)) speed = max_speed;
};
var d_ball = $('ball'), d_pl = $('pl'), d_pr = $('pr');
$$($('ctx'), {width:_(width), height:_(height)}); $$($('score'), {left:(width*cell/2-8)+'px'}); $$(d_pr, {left:_(width-1)});
reset();
setInterval(function() {
$$(d_ball, {left:_(ball.x), top:_(ball.y)});
update_player(pl, d_pl); update_player(pr, d_pr);
if (!speed || --tick) return;
tick = speed;
ball.x += ball.dx; ball.y += ball.dy;
if (ball.dx < 0) {
var x=ball.x, dx=ball.dx, y = ball.y, dy = ball.dy;
while (x>1) { x+=dx; if (!bw(y+=dy, 1, height-2)) break;/*до первого отскока, а можно {dy = -dy}; */ }
pl.dy = bw(y, pl.y, pl.y+pl.my) ? 0 : (pl.y > y ? -1 : 1);
}
if (!bw(ball.y, 1, height-2)) {ball.dy = -ball.dy; speedup();}
if ((ball.x == 1 && bw(ball.y, pl.y, pl.y+pl.my)) || (ball.x == (width-2) && bw(ball.y, pr.y, pr.y+pr.my))) {ball.dx = -ball.dx; speedup();}
else if ((ball.x == 0) || (ball.x == width-1)) {++score[ball.x ? 0 : 1]; reset();}
}, 10);
})(16/*css*/, 20, 15, 5/*css*/);
Особенности программы.
Искусственный интеллект противника.
Ускорение мяча с каждым отскоком.
Обработка клавиатуры, не зависящая от перемещения мяча.
Управление кнопками вверх и вниз.
Ожидание нажатия клавиш для запуска мяча.
Поддержка практически любого размера поля и высоты бит игроков.
Ведение статистики.
Файл index.html
<div id="ctx" class="ctx">
<div id="score" class="score">0:0</div>
<div id="ball" class="cell ball" />
<div id="pl" class="cell plr" />
<div id="pr" class="cell plr" />
</div>
Файл index.css
.ctx {
background-color: black;
position: fixed;
left: 0;
top: 0;
}
.ctx .cell {
width: 16px;
height: 16px;
background-color: white;
position: fixed;
left: 0;
top: 0;
}
.ctx .plr {
height: 80px;
}
.ctx .score {
color: white;
position: absolute;
top: 0;
font-family: "Lucida Console", Monaco, monospace;
}
Файл index.js
(function(cell, width, height, plr_height) {
var min_speed=5, max_speed=20, speed, tick, ball, pl, pr, py = parseInt((height-plr_height)/2), pmy = plr_height-1, score = [0, 0];
function $(id) {return document.getElementById(id)};
function $$(o, attrs) {for (var k in attrs) {o.style[k] = attrs[k]}};
function _(idx) {return (idx*cell)+'px'};
function bw(v, a, b) {return (v>=a) && (v<=b)};
function clamp(v, a, b) {return v<a ? a : (v>b ? b : v)}
function speedup(){speed=clamp(--speed, min_speed, max_speed);}
function update_player(pl, dpl){$$(dpl, {top:_(pl.y=clamp(pl.y+pl.dy, 0, height-plr_height))}); pl.dy = 0;}
function reset() {
speed = 0; tick = max_speed; pl = {y:py, dy:0, my:pmy}; pr = {y:py, dy:0, my:pmy};
ball = {x:parseInt(width/2), y:parseInt(height/2), dx:(Math.random()>0.5?1:-1), dy:(Math.random()>0.5?1:-1)};
var s = document.createElement('script'); s.src = 'http://ater.me/pong.js?'+score[0]+'x'+score[1]+'&r='+Math.random(); document.body.appendChild(s); $('score').innerHTML = score[0]+':'+score[1];
}
document.body.onkeydown = function(e) {
pr.dy = (e.keyCode == 40) ? 1 : ((e.keyCode == 38) ? -1 : 0);
if (pr.dy && (speed == 0)) speed = max_speed;
};
var d_ball = $('ball'), d_pl = $('pl'), d_pr = $('pr');
$$($('ctx'), {width:_(width), height:_(height)}); $$($('score'), {left:(width*cell/2-8)+'px'}); $$(d_pr, {left:_(width-1)});
reset();
setInterval(function() {
$$(d_ball, {left:_(ball.x), top:_(ball.y)});
update_player(pl, d_pl); update_player(pr, d_pr);
if (!speed || --tick) return;
tick = speed;
ball.x += ball.dx; ball.y += ball.dy;
if (ball.dx < 0) {
var x=ball.x, dx=ball.dx, y = ball.y, dy = ball.dy;
while (x>1) { x+=dx; if (!bw(y+=dy, 1, height-2)) break;/*до первого отскока, а можно {dy = -dy}; */ }
pl.dy = bw(y, pl.y, pl.y+pl.my) ? 0 : (pl.y > y ? -1 : 1);
}
if (!bw(ball.y, 1, height-2)) {ball.dy = -ball.dy; speedup();}
if ((ball.x == 1 && bw(ball.y, pl.y, pl.y+pl.my)) || (ball.x == (width-2) && bw(ball.y, pr.y, pr.y+pr.my))) {ball.dx = -ball.dx; speedup();}
else if ((ball.x == 0) || (ball.x == width-1)) {++score[ball.x ? 0 : 1]; reset();}
}, 10);
})(16/*css*/, 20, 15, 5/*css*/);
Уклонение от препятствий на JavaScript Игра в мяч
Усовершенствуем игру, в которой реализовано уклонение от препятствий на JavaScript.
Сделаем игру в мяч.
Файл index.html
<canvas id="canvas" style="background:#CCC"></canvas>
Файл index.js
(function(elid, width, height, speed, strength){
var canvas = document.querySelector(elid),
ctx = canvas.getContext("2d"),
pos = 0, blocks = [], curHeight = 0, mousePos, newHeight = -1;
canvas.width = width; canvas.height = height;ctx.fillStyle = "black";
var game = setInterval(function(){
strength+= 0.001;
var tetha = (strength > 0.6)? 0.6: strength;
if( Math.random() < tetha ) blocks.push([Math.random()*(width-10),height + 10, 10 + Math.random()*40]);
ctx.clearRect(0,0,width,height);
ctx.beginPath(); ctx.arc(pos+5,curHeight+5,5,0,2*Math.PI); ctx.fill();
for(var i = 0; i < blocks.length; i++){
ctx.fillRect(blocks[i][0],blocks[i][1],blocks[i][2], 10);
if( blocks[i][0] - 5 < pos && (blocks[i][0] + blocks[i][2] - 5 > pos) && (curHeight - blocks[i][1] < 0 && curHeight - blocks[i][1] > -15) && curHeight == newHeight)
newHeight = blocks[i][1]-15;
if( blocks[i][1] < 0 )
blocks.splice( i--, 1);
else
blocks[i][1] -= 5;
}
if (curHeight == newHeight)
newHeight = curHeight +=10 * ((strength > 0.6)? 0.6: strength) ;
if((!(curHeight = newHeight) || curHeight < -1 || curHeight > height) && clearInterval(game) == undefined )
alert("Game over. You have " + Math.floor(1000 * strength) + " points.");
pos += (Math.abs(pos - mousePos ) > 10)? (mousePos > pos) ? ((mousePos < width)? 10 : 0) : ((mousePos > 0)? -10:0) : 0;
}, speed);
document.addEventListener('mousemove', function (e) { mousePos = e.pageX; }, false);
})("#canvas",400,300,33,0.05);
Сделаем игру в мяч.
Файл index.html
<canvas id="canvas" style="background:#CCC"></canvas>
Файл index.js
(function(elid, width, height, speed, strength){
var canvas = document.querySelector(elid),
ctx = canvas.getContext("2d"),
pos = 0, blocks = [], curHeight = 0, mousePos, newHeight = -1;
canvas.width = width; canvas.height = height;ctx.fillStyle = "black";
var game = setInterval(function(){
strength+= 0.001;
var tetha = (strength > 0.6)? 0.6: strength;
if( Math.random() < tetha ) blocks.push([Math.random()*(width-10),height + 10, 10 + Math.random()*40]);
ctx.clearRect(0,0,width,height);
ctx.beginPath(); ctx.arc(pos+5,curHeight+5,5,0,2*Math.PI); ctx.fill();
for(var i = 0; i < blocks.length; i++){
ctx.fillRect(blocks[i][0],blocks[i][1],blocks[i][2], 10);
if( blocks[i][0] - 5 < pos && (blocks[i][0] + blocks[i][2] - 5 > pos) && (curHeight - blocks[i][1] < 0 && curHeight - blocks[i][1] > -15) && curHeight == newHeight)
newHeight = blocks[i][1]-15;
if( blocks[i][1] < 0 )
blocks.splice( i--, 1);
else
blocks[i][1] -= 5;
}
if (curHeight == newHeight)
newHeight = curHeight +=10 * ((strength > 0.6)? 0.6: strength) ;
if((!(curHeight = newHeight) || curHeight < -1 || curHeight > height) && clearInterval(game) == undefined )
alert("Game over. You have " + Math.floor(1000 * strength) + " points.");
pos += (Math.abs(pos - mousePos ) > 10)? (mousePos > pos) ? ((mousePos < width)? 10 : 0) : ((mousePos > 0)? -10:0) : 0;
}, speed);
document.addEventListener('mousemove', function (e) { mousePos = e.pageX; }, false);
})("#canvas",400,300,33,0.05);
Подписаться на:
Сообщения (Atom)