Файл main.js
window.document.addEventListener("route.changed", function (e) {
console.log(e.data.route, "------", e.data.params);
}, false);
Router.map("/foo", "route.changed");
Router.map("/foo/(bar-map)", "route.changed");
Router.map("/foo/(bar)/name", function (e){
console.log("Direct callback", e);
});
Router.start();
Файл router.js
(function($, win) {
var Routes = [];
var doc = win.document;
function fireCustomEvent(name, data) {
var evt = doc.createEvent("HTMLEvents");
evt.initEvent(name, true, true ); // event type,bubbling,cancelable
evt.data = data;
return !doc.dispatchEvent(evt);
}
function hashChange() {
var url = (win.location.hash || "#").slice(1);
$.Router.match(url);
}
$.Router = {
map: function (r, e) {
// Escape anything except () and replace (name) with (.+)
var es_tmp = r.replace(/[-[\]{}*+?.,\\^$|#\s]/g, "\\$&").replace(/\([^\)]+\)/g, "([^/]+)");
var r_exp = new RegExp("^"+es_tmp+"$", "g");
// Save names and routes under with expression and variable names
Routes.push({
exp: r_exp,
names: r_exp.exec(r).slice(1),
event: e
});
},
match: function (r) {
for(var i in Routes) {
var rout = Routes[i];
if (r.match(rout.exp)){
var params = {};
info = rout.exp.exec(r).slice(1);
for(var k in info) {
params[rout.names[k].replace(/[()]/g, "")] = info[k];
}
if(typeof rout.event === 'function') {
rout.event({data: {params: params, route: r}});
}else if( typeof rout.event === 'string') {
fireCustomEvent(rout.event, {params: params, route: r});
}
}
}
},
start: function() {
if (window.addEventListener) {
window.addEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
window.attachEvent("onhashchange", hashChange);
}
hashChange(); // Call first time for loading first URL
},
stop: function() {
if (window.removeEventListener) {
window.removeEventListener("hashchange", hashChange, false);
}
else if (window.attachEvent) {
window.detachEvent("onhashchange", hashChange);
}
}
};
})(window, window);
вторник, 31 декабря 2013 г.
JavaSript Simple Router
var Router = (function () {
var Router = function () {};
Router.prototype.add = function (path, callback) {
this.routes = this.routes || [];
this.routes.push({
path: new RegExp(path.replace(/\//g, "\\/").replace(/:(\w*)/g,"(\\w*)"))
, callback: callback
});
};
Router.prototype.process = function () {
var route
, params;
for (route in this.routes) {
params = window.location.pathname.match(route.path);
if (params) {
route.callback(params);
return;
}
}
};
return Router;
})();
Router = new Router();
Router.add('/blog/:id', blogCallback);
function blogCallback () {
alert('OK');
}
$(document).ready(function(){
Router.process();
});
var Router = function () {};
Router.prototype.add = function (path, callback) {
this.routes = this.routes || [];
this.routes.push({
path: new RegExp(path.replace(/\//g, "\\/").replace(/:(\w*)/g,"(\\w*)"))
, callback: callback
});
};
Router.prototype.process = function () {
var route
, params;
for (route in this.routes) {
params = window.location.pathname.match(route.path);
if (params) {
route.callback(params);
return;
}
}
};
return Router;
})();
Router = new Router();
Router.add('/blog/:id', blogCallback);
function blogCallback () {
alert('OK');
}
$(document).ready(function(){
Router.process();
});
понедельник, 30 декабря 2013 г.
JavaScript Улучшенные Web Worker
Web-worker - это JavaScript-технология, позволяющая запускать изолированные участки кода в отдельном потоке. Код из web worker не тормозит работу графического интерфейса и выполняется быстрее, чем код на странице, что делает использование web worker очень привлекательным для ресурсоёмких расчётов вроде рисования графики или криптографии.
Обычно, если в web worker всего одна функция, то мы просто пишем:
- в коде страницы (снаружи):
var worker = new Worker("myscript.js");
worker.onmessage (event.data){workerСallback(event.data);}
function workerСallback(data){ /*do something with data object;*/}
worker.postMessage({some:"some"});
- в коде web worker (внутри):
onmessage = function (event) {postMessage(mySingleFunction(event.data));}
Однако, если в web worker добавить ещё одну функцию, вызываемую извне, то количество кода, обеспечивающего вызов этих функций возрастёт и он будет выглядеть уже не так изящно:
- в коде страницы (снаружи):
function firstFunctionСallback(data){ /*do something with data object;*/}
function secondFunctionСallback(data){ }
worker.onmessage (msg){
if(msg.data.callback == "firstFunctionСallback"){
firstFunctionСallback(msg.data.result);
}
if(msg.data.callback == "secondFunctionСallback"){
firstFunctionСallback(msg.data.result);
}
}
worker.postMessage({functionName: "firstFunction", data: data);
- в коде web worker (внутри):
onmessage = function (event) {
var functionName = event.data.functionName;
if(functionName == "firstFinction"){
postMessage({callback: "firstFunctionСallback", result: firstFinction(event.data.data)});
}
if(functionName == "secondFunction"){
postMessage({callback: "secondFunctionСallback", result: secondFunction(event.data.data)});
}
...
}
При таком подходе нет возможности использовать в качестве коллбэков анонимные функции и при каждом добавлении новой функции в web worker нужно каждый раз снаружи и внутри web worker писать некоторое количество дополнительного кода, обеспечивающего вызов этой функции.
Чтобы избежать этого web worker можно обернуть в объект, который будет выполнять эту работу.
Назовём такой объект Performer и разместим его в коде страницы (снаружи):
function Performer(scriptSource) {
var worker = new Worker(scriptSource), callbacks = {}, nextRequestId = 0;
this.perform = function(functionName, params, callback) {
callbacks["request_" + (++nextRequestId)] = callback;
worker.postMessage(
{functionName: functionName, params: params, requestId: nextRequestId}
);
}
worker.onmessage = function(msg) {
callbacks["request_" + msg.data.requestId](msg.data.result);
delete callbacks["request_" + msg.data.requestId];
}
}
Во внутреннем коде web worker изменим обработчик внешних сообщений:
onmessage = function (event) {
var requestId = event.data.requestId;
var workerFunction = eval(event.data.functionName);
var params = event.data.params;
var result = workerFunction(params);
postMessage({result: result, requestId: requestId});
}
Теперь можно добавлять в web worker любые функции и вызывать их извне без написания вспомогательного кода, а также использовать анонимные функции в коллбэках:
var performer = new Performer("myscript.js");
performer.perform("firstFunction", {some: "some"}, function(result){console.log("result1="+result);});
performer.perform("secondFunction", {some: "some"}, function(result){console.log("result2="+result);});
Ели web worker размещён не в отдельном файле скрипта, а встроен в страницу,
то в коде перформера должны быть учтены кроссбраузерные различия.
С их учётом часть перформера, которая отвечает за инициализацию web worker, будет выглядеть так:
function Performer(scriptText) {
var worker = null;
try {// Firefox
var Url = window.webkitURL || window.URL;
worker = new Worker(Url.createObjectURL(new Blob([ scriptText ])));
} catch (browserNotSupportWindowUrl) {
try {// Chrome
worker = new Worker('data:application/javascript,' + encodeURIComponent(scriptText));
} catch (browserNotSupportWorkers) {// Opera
eval(scriptText);
worker = {
postMessage : function(data) {
var workerFunction = eval(data.functionName);
worker.onmessage({
data : {
result : workerFunction(data.params),
requestId : data.requestId
}
});
}
};
}
}
...
}
а создание так:
var performer = new Performer($('#myscript').text());
Таким образом, даже в браузерах, не поддерживающих web worker, код web worker всё равно будет выполняться, просто медленнее.
JavaScript Build a vertical scrolling shooter game with HTML5 canvas
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML5 Canvas Game Part 6 || Atomic Robot Design</title>
<link href='http://fonts.googleapis.com/css?family=VT323' rel='stylesheet' />
<style>
body {
padding:0;
margin:0;
background:#666;
}
canvas {
display:block;
margin:30px auto 0;
border:1px dashed #ccc;
background:#000;
}
</style>
<script>
var canvas,
ctx,
width = 600,
height = 600,
enemyTotal = 5,
enemies = [],
enemy_x = 50,
enemy_y = -45,
enemy_w = 50,
enemy_h = 38,
speed = 3,
enemy,
rightKey = false,
leftKey = false,
upKey = false,
downKey = false,
ship,
ship_x = (width / 2) - 25, ship_y = height - 75, ship_w = 50, ship_h = 57,
laserTotal = 2,
lasers = [],
score = 0,
alive = true,
lives = 3,
starfield,
starX = 0, starY = 0, starY2 = -600,
gameStarted = false;
//Array to hold all the enemies on screen
for (var i = 0; i < enemyTotal; i++) {
enemies.push([enemy_x, enemy_y, enemy_w, enemy_h, speed]);
enemy_x += enemy_w + 60;
}
//Clears the canvas so it can be updated
function clearCanvas() {
ctx.clearRect(0,0,width,height);
}
//Cycles through the array and draws the updated enemy position
function drawEnemies() {
for (var i = 0; i < enemies.length; i++) {
ctx.drawImage(enemy, enemies[i][0], enemies[i][1]);
}
}
//If an arrow key is being pressed, moves the ship in the right direction
function drawShip() {
if (rightKey) ship_x += 5;
else if (leftKey) ship_x -= 5;
if (upKey) ship_y -= 5;
else if (downKey) ship_y += 5;
if (ship_x <= 0) ship_x = 0;
if ((ship_x + ship_w) >= width) ship_x = width - ship_w;
if (ship_y <= 0) ship_y = 0;
if ((ship_y + ship_h) >= height) ship_y = height - ship_h;
ctx.drawImage(ship, ship_x, ship_y);
}
//This moves the enemies downwards on the canvas and if one passes the bottom of the canvas, it moves it to above the canvas
function moveEnemies() {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i][1] < height) {
enemies[i][1] += enemies[i][4];
} else if (enemies[i][1] > height - 1) {
enemies[i][1] = -45;
}
}
}
//If there are lasers in the lasers array, then this will draw them on the canvas
function drawLaser() {
if (lasers.length)
for (var i = 0; i < lasers.length; i++) {
ctx.fillStyle = '#f00';
ctx.fillRect(lasers[i][0],lasers[i][1],lasers[i][2],lasers[i][3])
}
}
//If we're drawing lasers on the canvas, this moves them up the canvas
function moveLaser() {
for (var i = 0; i < lasers.length; i++) {
if (lasers[i][1] > -11) {
lasers[i][1] -= 10;
} else if (lasers[i][1] < -10) {
lasers.splice(i, 1);
}
}
}
//Runs a couple of loops to see if any of the lasers have hit any of the enemies
function hitTest() {
var remove = false;
for (var i = 0; i < lasers.length; i++) {
for (var j = 0; j < enemies.length; j++) {
if (lasers[i][1] <= (enemies[j][1] + enemies[j][3]) && lasers[i][0] >= enemies[j][0] && lasers[i][0] <= (enemies[j][0] + enemies[j][2])) {
remove = true;
enemies.splice(j, 1);
score += 10;
enemies.push([(Math.random() * 500) + 50, -45, enemy_w, enemy_h, speed]);
}
}
if (remove == true) {
lasers.splice(i, 1);
remove = false;
}
}
}
//Similar to the laser hit test, this function checks to see if the player's ship collides with any of the enemies
function shipCollision() {
var ship_xw = ship_x + ship_w,
ship_yh = ship_y + ship_h;
for (var i = 0; i < enemies.length; i++) {
if (ship_x > enemies[i][0] && ship_x < enemies[i][0] + enemy_w && ship_y > enemies[i][1] && ship_y < enemies[i][1] + enemy_h) {
checkLives();
}
if (ship_xw < enemies[i][0] + enemy_w && ship_xw > enemies[i][0] && ship_y > enemies[i][1] && ship_y < enemies[i][1] + enemy_h) {
checkLives();
}
if (ship_yh > enemies[i][1] && ship_yh < enemies[i][1] + enemy_h && ship_x > enemies[i][0] && ship_x < enemies[i][0] + enemy_w) {
checkLives();
}
if (ship_yh > enemies[i][1] && ship_yh < enemies[i][1] + enemy_h && ship_xw < enemies[i][0] + enemy_w && ship_xw > enemies[i][0]) {
checkLives();
}
}
}
//This function runs whenever the player's ship hits an enemy and either subtracts a life or sets the alive variable to false if the player runs out of lives
function checkLives() {
lives -= 1;
if (lives > 0) {
reset();
} else if (lives == 0) {
alive = false;
}
}
//This simply resets the ship and enemies to their starting positions
function reset() {
var enemy_reset_x = 50;
ship_x = (width / 2) - 25, ship_y = height - 75, ship_w = 50, ship_h = 57;
for (var i = 0; i < enemies.length; i++) {
enemies[i][0] = enemy_reset_x;
enemies[i][1] = -45;
enemy_reset_x = enemy_reset_x + enemy_w + 60;
}
}
//After the player loses all their lives, the continue button is shown and if clicked, it resets the game and removes the event listener for the continue button
function continueButton(e) {
var cursorPos = getCursorPos(e);
if (cursorPos.x > (width / 2) - 53 && cursorPos.x < (width / 2) + 47 && cursorPos.y > (height / 2) + 10 && cursorPos.y < (height / 2) + 50) {
alive = true;
lives = 3;
reset();
canvas.removeEventListener('click', continueButton, false);
}
}
//holds the cursors position
function cursorPosition(x,y) {
this.x = x;
this.y = y;
}
//finds the cursor's position after the mouse is clicked
function getCursorPos(e) {
var x;
var y;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
} else {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
var cursorPos = new cursorPosition(x, y);
return cursorPos;
}
//Draws the text for the score and lives on the canvas and if the player runs out of lives, it's draws the game over text and continue button as well as adding the event listener for the continue button
function scoreTotal() {
ctx.font = 'bold 20px VT323';
ctx.fillStyle = '#fff';
ctx.fillText('Score: ', 10, 55);
ctx.fillText(score, 70, 55);
ctx.fillText('Lives:', 10, 30);
ctx.fillText(lives, 68, 30);
if (!gameStarted) {
ctx.font = 'bold 50px VT323';
ctx.fillText('Canvas Shooter', width / 2 - 150, height / 2);
ctx.font = 'bold 20px VT323';
ctx.fillText('Click to Play', width / 2 - 56, height / 2 + 30);
ctx.fillText('Use arrow keys to move', width / 2 - 100, height / 2 + 60);
ctx.fillText('Use the x key to shoot', width / 2 - 100, height / 2 + 90);
}
if (!alive) {
ctx.fillText('Game Over!', 245, height / 2);
ctx.fillRect((width / 2) - 60, (height / 2) + 10,100,40);
ctx.fillStyle = '#000';
ctx.fillText('Continue?', 250, (height / 2) + 35);
canvas.addEventListener('click', continueButton, false);
}
}
//Draws and animates the background starfield
function drawStarfield() {
ctx.drawImage(starfield,starX,starY);
ctx.drawImage(starfield,starX,starY2);
if (starY > 600) {
starY = -599;
}
if (starY2 > 600) {
starY2 = -599;
}
starY += 1;
starY2 += 1;
}
//The initial function called when the page first loads. Loads the ship, enemy and starfield images and adds the event listeners for the arrow keys. It then calls the gameLoop function.
function init() {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
enemy = new Image();
enemy.src = '8bit_enemy.png';
ship = new Image();
ship.src = 'ship.png';
starfield = new Image();
starfield.src = 'starfield.jpg';
document.addEventListener('keydown', keyDown, false);
document.addEventListener('keyup', keyUp, false);
canvas.addEventListener('click', gameStart, false);
gameLoop();
}
function gameStart() {
gameStarted = true;
canvas.removeEventListener('click', gameStart, false);
}
//The main function of the game, it calls all the other functions needed to make the game run
function gameLoop() {
clearCanvas();
drawStarfield()
if (alive && gameStarted && lives > 0) {
hitTest();
shipCollision();
moveLaser();
moveEnemies();
drawEnemies();
drawShip();
drawLaser();
}
scoreTotal();
game = setTimeout(gameLoop, 1000 / 30);
}
//Checks to see which key has been pressed and either to move the ship or fire a laser
function keyDown(e) {
if (e.keyCode == 39) rightKey = true;
else if (e.keyCode == 37) leftKey = true;
if (e.keyCode == 38) upKey = true;
else if (e.keyCode == 40) downKey = true;
if (e.keyCode == 88 && lasers.length <= laserTotal) lasers.push([ship_x + 25, ship_y - 20, 4, 20]);
}
//Checks to see if a pressed key has been released and stops the ships movement if it has
function keyUp(e) {
if (e.keyCode == 39) rightKey = false;
else if (e.keyCode == 37) leftKey = false;
if (e.keyCode == 38) upKey = false;
else if (e.keyCode == 40) downKey = false;
}
window.onload = init;
</script>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
</html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>HTML5 Canvas Game Part 6 || Atomic Robot Design</title>
<link href='http://fonts.googleapis.com/css?family=VT323' rel='stylesheet' />
<style>
body {
padding:0;
margin:0;
background:#666;
}
canvas {
display:block;
margin:30px auto 0;
border:1px dashed #ccc;
background:#000;
}
</style>
<script>
var canvas,
ctx,
width = 600,
height = 600,
enemyTotal = 5,
enemies = [],
enemy_x = 50,
enemy_y = -45,
enemy_w = 50,
enemy_h = 38,
speed = 3,
enemy,
rightKey = false,
leftKey = false,
upKey = false,
downKey = false,
ship,
ship_x = (width / 2) - 25, ship_y = height - 75, ship_w = 50, ship_h = 57,
laserTotal = 2,
lasers = [],
score = 0,
alive = true,
lives = 3,
starfield,
starX = 0, starY = 0, starY2 = -600,
gameStarted = false;
//Array to hold all the enemies on screen
for (var i = 0; i < enemyTotal; i++) {
enemies.push([enemy_x, enemy_y, enemy_w, enemy_h, speed]);
enemy_x += enemy_w + 60;
}
//Clears the canvas so it can be updated
function clearCanvas() {
ctx.clearRect(0,0,width,height);
}
//Cycles through the array and draws the updated enemy position
function drawEnemies() {
for (var i = 0; i < enemies.length; i++) {
ctx.drawImage(enemy, enemies[i][0], enemies[i][1]);
}
}
//If an arrow key is being pressed, moves the ship in the right direction
function drawShip() {
if (rightKey) ship_x += 5;
else if (leftKey) ship_x -= 5;
if (upKey) ship_y -= 5;
else if (downKey) ship_y += 5;
if (ship_x <= 0) ship_x = 0;
if ((ship_x + ship_w) >= width) ship_x = width - ship_w;
if (ship_y <= 0) ship_y = 0;
if ((ship_y + ship_h) >= height) ship_y = height - ship_h;
ctx.drawImage(ship, ship_x, ship_y);
}
//This moves the enemies downwards on the canvas and if one passes the bottom of the canvas, it moves it to above the canvas
function moveEnemies() {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i][1] < height) {
enemies[i][1] += enemies[i][4];
} else if (enemies[i][1] > height - 1) {
enemies[i][1] = -45;
}
}
}
//If there are lasers in the lasers array, then this will draw them on the canvas
function drawLaser() {
if (lasers.length)
for (var i = 0; i < lasers.length; i++) {
ctx.fillStyle = '#f00';
ctx.fillRect(lasers[i][0],lasers[i][1],lasers[i][2],lasers[i][3])
}
}
//If we're drawing lasers on the canvas, this moves them up the canvas
function moveLaser() {
for (var i = 0; i < lasers.length; i++) {
if (lasers[i][1] > -11) {
lasers[i][1] -= 10;
} else if (lasers[i][1] < -10) {
lasers.splice(i, 1);
}
}
}
//Runs a couple of loops to see if any of the lasers have hit any of the enemies
function hitTest() {
var remove = false;
for (var i = 0; i < lasers.length; i++) {
for (var j = 0; j < enemies.length; j++) {
if (lasers[i][1] <= (enemies[j][1] + enemies[j][3]) && lasers[i][0] >= enemies[j][0] && lasers[i][0] <= (enemies[j][0] + enemies[j][2])) {
remove = true;
enemies.splice(j, 1);
score += 10;
enemies.push([(Math.random() * 500) + 50, -45, enemy_w, enemy_h, speed]);
}
}
if (remove == true) {
lasers.splice(i, 1);
remove = false;
}
}
}
//Similar to the laser hit test, this function checks to see if the player's ship collides with any of the enemies
function shipCollision() {
var ship_xw = ship_x + ship_w,
ship_yh = ship_y + ship_h;
for (var i = 0; i < enemies.length; i++) {
if (ship_x > enemies[i][0] && ship_x < enemies[i][0] + enemy_w && ship_y > enemies[i][1] && ship_y < enemies[i][1] + enemy_h) {
checkLives();
}
if (ship_xw < enemies[i][0] + enemy_w && ship_xw > enemies[i][0] && ship_y > enemies[i][1] && ship_y < enemies[i][1] + enemy_h) {
checkLives();
}
if (ship_yh > enemies[i][1] && ship_yh < enemies[i][1] + enemy_h && ship_x > enemies[i][0] && ship_x < enemies[i][0] + enemy_w) {
checkLives();
}
if (ship_yh > enemies[i][1] && ship_yh < enemies[i][1] + enemy_h && ship_xw < enemies[i][0] + enemy_w && ship_xw > enemies[i][0]) {
checkLives();
}
}
}
//This function runs whenever the player's ship hits an enemy and either subtracts a life or sets the alive variable to false if the player runs out of lives
function checkLives() {
lives -= 1;
if (lives > 0) {
reset();
} else if (lives == 0) {
alive = false;
}
}
//This simply resets the ship and enemies to their starting positions
function reset() {
var enemy_reset_x = 50;
ship_x = (width / 2) - 25, ship_y = height - 75, ship_w = 50, ship_h = 57;
for (var i = 0; i < enemies.length; i++) {
enemies[i][0] = enemy_reset_x;
enemies[i][1] = -45;
enemy_reset_x = enemy_reset_x + enemy_w + 60;
}
}
//After the player loses all their lives, the continue button is shown and if clicked, it resets the game and removes the event listener for the continue button
function continueButton(e) {
var cursorPos = getCursorPos(e);
if (cursorPos.x > (width / 2) - 53 && cursorPos.x < (width / 2) + 47 && cursorPos.y > (height / 2) + 10 && cursorPos.y < (height / 2) + 50) {
alive = true;
lives = 3;
reset();
canvas.removeEventListener('click', continueButton, false);
}
}
//holds the cursors position
function cursorPosition(x,y) {
this.x = x;
this.y = y;
}
//finds the cursor's position after the mouse is clicked
function getCursorPos(e) {
var x;
var y;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
} else {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
var cursorPos = new cursorPosition(x, y);
return cursorPos;
}
//Draws the text for the score and lives on the canvas and if the player runs out of lives, it's draws the game over text and continue button as well as adding the event listener for the continue button
function scoreTotal() {
ctx.font = 'bold 20px VT323';
ctx.fillStyle = '#fff';
ctx.fillText('Score: ', 10, 55);
ctx.fillText(score, 70, 55);
ctx.fillText('Lives:', 10, 30);
ctx.fillText(lives, 68, 30);
if (!gameStarted) {
ctx.font = 'bold 50px VT323';
ctx.fillText('Canvas Shooter', width / 2 - 150, height / 2);
ctx.font = 'bold 20px VT323';
ctx.fillText('Click to Play', width / 2 - 56, height / 2 + 30);
ctx.fillText('Use arrow keys to move', width / 2 - 100, height / 2 + 60);
ctx.fillText('Use the x key to shoot', width / 2 - 100, height / 2 + 90);
}
if (!alive) {
ctx.fillText('Game Over!', 245, height / 2);
ctx.fillRect((width / 2) - 60, (height / 2) + 10,100,40);
ctx.fillStyle = '#000';
ctx.fillText('Continue?', 250, (height / 2) + 35);
canvas.addEventListener('click', continueButton, false);
}
}
//Draws and animates the background starfield
function drawStarfield() {
ctx.drawImage(starfield,starX,starY);
ctx.drawImage(starfield,starX,starY2);
if (starY > 600) {
starY = -599;
}
if (starY2 > 600) {
starY2 = -599;
}
starY += 1;
starY2 += 1;
}
//The initial function called when the page first loads. Loads the ship, enemy and starfield images and adds the event listeners for the arrow keys. It then calls the gameLoop function.
function init() {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
enemy = new Image();
enemy.src = '8bit_enemy.png';
ship = new Image();
ship.src = 'ship.png';
starfield = new Image();
starfield.src = 'starfield.jpg';
document.addEventListener('keydown', keyDown, false);
document.addEventListener('keyup', keyUp, false);
canvas.addEventListener('click', gameStart, false);
gameLoop();
}
function gameStart() {
gameStarted = true;
canvas.removeEventListener('click', gameStart, false);
}
//The main function of the game, it calls all the other functions needed to make the game run
function gameLoop() {
clearCanvas();
drawStarfield()
if (alive && gameStarted && lives > 0) {
hitTest();
shipCollision();
moveLaser();
moveEnemies();
drawEnemies();
drawShip();
drawLaser();
}
scoreTotal();
game = setTimeout(gameLoop, 1000 / 30);
}
//Checks to see which key has been pressed and either to move the ship or fire a laser
function keyDown(e) {
if (e.keyCode == 39) rightKey = true;
else if (e.keyCode == 37) leftKey = true;
if (e.keyCode == 38) upKey = true;
else if (e.keyCode == 40) downKey = true;
if (e.keyCode == 88 && lasers.length <= laserTotal) lasers.push([ship_x + 25, ship_y - 20, 4, 20]);
}
//Checks to see if a pressed key has been released and stops the ships movement if it has
function keyUp(e) {
if (e.keyCode == 39) rightKey = false;
else if (e.keyCode == 37) leftKey = false;
if (e.keyCode == 38) upKey = false;
else if (e.keyCode == 40) downKey = false;
}
window.onload = init;
</script>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
</body>
</html>
JavaScript Определение типа элемента
function getClass (element) {
return Object.prototype.toString.call(element).slice(8, -1);
}
var a = '';
console.log(getClass(a)); // String
return Object.prototype.toString.call(element).slice(8, -1);
}
var a = '';
console.log(getClass(a)); // String
JavaScript Примитивные типы
В JavaScript существует несколько примитивных типов:
null - Специальный тип, содержит только значение null.
undefined - Специальный тип, содержит только значение undefined.
number - Числа: 0, 3.14, а также значения NaN и Infinity
boolean - true, false.
string - Строки, такие как "Мяу" или пустая строка "".
Все остальные значения являются объектами, включая функции и массивы.
null - Специальный тип, содержит только значение null.
undefined - Специальный тип, содержит только значение undefined.
number - Числа: 0, 3.14, а также значения NaN и Infinity
boolean - true, false.
string - Строки, такие как "Мяу" или пустая строка "".
Все остальные значения являются объектами, включая функции и массивы.
JavaScript Создание функции из строки new Function()
Существует ещё один способ создания функции. Этот способ используется очень редко, но в отдельных случаях бывает весьма полезен, так как позволяет конструировать функцию во время выполнения программы, к примеру из данных, полученных с сервера или от пользователя. Он выглядит так:
var sum = new Function('a,b', ' return a+b; ');
var result = sum(1,2);
alert(result); // 3
То есть, функция создаётся вызовом new Function(params, code):
params - Параметры функции через запятую в виде строки.
code - Код функции в виде строки.
var sum = new Function('a,b', ' return a+b; ');
var result = sum(1,2);
alert(result); // 3
То есть, функция создаётся вызовом new Function(params, code):
params - Параметры функции через запятую в виде строки.
code - Код функции в виде строки.
В JavaScript функции можно объявлять условно
var age = prompt('Сколько вам лет?');
var sayHi;
if (age >= 18) {
sayHi = function() { alert('Вход разрешен'); }
} else {
sayHi = function() { alert('Извините, вы слишком молоды'); }
}
sayHi(); // запустит ту функцию, которая присвоена в if
var sayHi;
if (age >= 18) {
sayHi = function() { alert('Вход разрешен'); }
} else {
sayHi = function() { alert('Извините, вы слишком молоды'); }
}
sayHi(); // запустит ту функцию, которая присвоена в if
Устаревший getYear()
Некоторые браузеры реализуют нестандартный метод getYear(). Где-то он возвращает только 2 цифры из года, где-то 4. Так или иначе, этот метод отсутствует в стандарте JavaScript. Не используйте его. Для получения года есть getFullYear().
Пример.
var date = new Date();
var year = date.getFullYear();
console.log(year);
Пример.
var date = new Date();
var year = date.getFullYear();
console.log(year);
среда, 25 декабря 2013 г.
JavaScript Преобразование данных
В JavaScript есть три преобразования:
Строковое: String(value) - в строковом контексте или при сложении со строкой: '' + 1 или 1 + ''.
Численное: Number(value) - в численном контексте, включая унарный плюс +value.
Логическое: Boolean(value) - в логическом контексте, можно также сделать двойным НЕ: !!value.
Строковое: String(value) - в строковом контексте или при сложении со строкой: '' + 1 или 1 + ''.
Численное: Number(value) - в численном контексте, включая унарный плюс +value.
Логическое: Boolean(value) - в логическом контексте, можно также сделать двойным НЕ: !!value.
JavaScript Build tree from array
Построение дерева посредством JavaScript из плоского массива данных.
”http://jsfiddle.net/elado/yxmKZ/embedded/js,css,result”
Файл index.js
var treeData=[
{"id":1,"parentId":null,"title":"1"},
{"id":2,"parentId":1,"title":"1.1"},
{"id":3,"parentId":null,"title":"2"},
{"id":4,"parentId":1,"title":"1.2"},
{"id":5,"parentId":1,"title":"1.3"},
{"id":6,"parentId":4,"title":"1.2.1"},
{"id":7,"parentId":4,"title":"1.2.2"},
{"id":8,"parentId":4,"title":"1.2.3"},
{"id":9,"parentId":null,"title":"3"},
{"id":10,"parentId":9,"title":"3.1"},
{"id":11,"parentId":10,"title":"3.1.1"},
{"id":12,"parentId":11,"title":"3.1.1.1"},
{"id":13,"parentId":11,"title":"3.1.1.2"},
{"id":14,"parentId":11,"title":"3.1.1.3"},
{"id":15,"parentId":13,"title":"3.1.1.2.1"}
];
function NodeItem(data) {
this.data=data;
var container=document.createElement("li"),
title=container.appendChild(document.createElement("div"));
title.innerHTML=this.data.title;
this.element=container;
}
NodeItem.prototype= {
// lazy children element creation - not all nodes have children
getChildrenElement:function () {
return this._childElement = this._childElement || this.element.appendChild(document.createElement("ul"));
}
};
// convert all nodes to NodeItem instance
var nodeCollection=treeData.map(function (node) { return new NodeItem(node); });
// for faster lookup, store all nodes by their id
var nodesById={};
for (var i=0;i<nodeCollection.length;i++) nodesById[nodeCollection[i].data.id]=nodeCollection[i];
var rootNodeItemsContainer=document.createElement("ul");
// the magic happens here:
// every node finds its parent (by the id), and it's being adopted by the parent's children element
// that, actually, builds the tree, before it's in the document
// all root nodes are appended to a root container which is appended to an element on the document
for (var i=0;i<nodeCollection.length;i++) {
var node=nodeCollection[i];
var parentElement=node.data.parentId ? nodesById[node.data.parentId].getChildrenElement() : rootNodeItemsContainer;
parentElement.appendChild(node.element);
}
document.body.appendChild(rootNodeItemsContainer);
Файл index.css
* { font-family:lucida grande, arial; font-size:11px; }
ul {
padding:3px;
border:1px solid #aaa;
}
ul li {
padding:5px;
background-color:#eee;
}
ul li > div { padding:3px; margin-bottom:5px; background-color:#ccc; }
ul li > div + ul { margin-top:5px; }
ul li ul { margin-left:15px; }
”http://jsfiddle.net/elado/yxmKZ/embedded/js,css,result”
Файл index.js
var treeData=[
{"id":1,"parentId":null,"title":"1"},
{"id":2,"parentId":1,"title":"1.1"},
{"id":3,"parentId":null,"title":"2"},
{"id":4,"parentId":1,"title":"1.2"},
{"id":5,"parentId":1,"title":"1.3"},
{"id":6,"parentId":4,"title":"1.2.1"},
{"id":7,"parentId":4,"title":"1.2.2"},
{"id":8,"parentId":4,"title":"1.2.3"},
{"id":9,"parentId":null,"title":"3"},
{"id":10,"parentId":9,"title":"3.1"},
{"id":11,"parentId":10,"title":"3.1.1"},
{"id":12,"parentId":11,"title":"3.1.1.1"},
{"id":13,"parentId":11,"title":"3.1.1.2"},
{"id":14,"parentId":11,"title":"3.1.1.3"},
{"id":15,"parentId":13,"title":"3.1.1.2.1"}
];
function NodeItem(data) {
this.data=data;
var container=document.createElement("li"),
title=container.appendChild(document.createElement("div"));
title.innerHTML=this.data.title;
this.element=container;
}
NodeItem.prototype= {
// lazy children element creation - not all nodes have children
getChildrenElement:function () {
return this._childElement = this._childElement || this.element.appendChild(document.createElement("ul"));
}
};
// convert all nodes to NodeItem instance
var nodeCollection=treeData.map(function (node) { return new NodeItem(node); });
// for faster lookup, store all nodes by their id
var nodesById={};
for (var i=0;i<nodeCollection.length;i++) nodesById[nodeCollection[i].data.id]=nodeCollection[i];
var rootNodeItemsContainer=document.createElement("ul");
// the magic happens here:
// every node finds its parent (by the id), and it's being adopted by the parent's children element
// that, actually, builds the tree, before it's in the document
// all root nodes are appended to a root container which is appended to an element on the document
for (var i=0;i<nodeCollection.length;i++) {
var node=nodeCollection[i];
var parentElement=node.data.parentId ? nodesById[node.data.parentId].getChildrenElement() : rootNodeItemsContainer;
parentElement.appendChild(node.element);
}
document.body.appendChild(rootNodeItemsContainer);
Файл index.css
* { font-family:lucida grande, arial; font-size:11px; }
ul {
padding:3px;
border:1px solid #aaa;
}
ul li {
padding:5px;
background-color:#eee;
}
ul li > div { padding:3px; margin-bottom:5px; background-color:#ccc; }
ul li > div + ul { margin-top:5px; }
ul li ul { margin-left:15px; }
четверг, 19 декабря 2013 г.
NPM JavaScript - Частые команды по работе с Node Package Manager
Установка пакетов в Node Package Manager.
Устанавливает пакет express.
npm install express
Устанавливает все пакеты, перечисленные в package.json.
npm install
Устанавливает express и вносит запись о нем в package.json в секцию dependencies.
npm install express --save
Устанавливает grunt и вносит запись о нем в package.json в секцию devDependencies.
npm install grunt --save-dev
Варианты с --save и --save-dev сделают запись в package.json только, если он уже существует.
Чтобы каждый раз не указывать --save, можно прописать:
npm config set save true
Теперь все устанавливаемый пакеты будут автоматом прописываться в package.json.
Обычно получается быстрее отредактировать package.json, удалить ненужные пакеты, добавить новые, подкорректировать версии и сделать
npm prune
npm install
чем ставить - удалять каждый пакет отдельно.
Сокращенные варианты команд.
Для ускорения процесса ввода команд удобно использовать сокращения:
Ключ Сокращение
install i
uninstall r
config c
update up
list ls
--save -S
--save-dev -D
Пример.
npm install express --save
и то же самое
npm i express -S
Подготовка к npm init
Не очень удобно при создании package.json при помощи npm init каждый раз вводить персональные данные. Чтобы этого избежать, сделаем настройку:
Внесем информацию об авторе "по умолчанию".
npm set init.author.name "$NAME"
npm set init.author.email "$EMAIL"
npm set init.author.url "$SITE"
Вместо переменных среды $NAME и тому подобных можно внести сами данные.
Данная команда выведет список всех возможных настроек.
npm config ls -l
Проверить, не устарели ли пакеты.
npm outdated
Фиксируем версии пакетов.
npm shrinkwrap
Прежде чем передавать продукт в промышленную эксплуатацию, по хорошему, нужно указать в package.json точные версии пакетов с которыми все 100% работает. Эта команда так и сделает. После ее выполнения в вашем package.json все версии пакетов будут жестко зафиксированы.
Обновление версии NPM.
npm update npm -g
Устанавливает пакет express.
npm install express
Устанавливает все пакеты, перечисленные в package.json.
npm install
Устанавливает express и вносит запись о нем в package.json в секцию dependencies.
npm install express --save
Устанавливает grunt и вносит запись о нем в package.json в секцию devDependencies.
npm install grunt --save-dev
Варианты с --save и --save-dev сделают запись в package.json только, если он уже существует.
Чтобы каждый раз не указывать --save, можно прописать:
npm config set save true
Теперь все устанавливаемый пакеты будут автоматом прописываться в package.json.
Обычно получается быстрее отредактировать package.json, удалить ненужные пакеты, добавить новые, подкорректировать версии и сделать
npm prune
npm install
чем ставить - удалять каждый пакет отдельно.
Сокращенные варианты команд.
Для ускорения процесса ввода команд удобно использовать сокращения:
Ключ Сокращение
install i
uninstall r
config c
update up
list ls
--save -S
--save-dev -D
Пример.
npm install express --save
и то же самое
npm i express -S
Подготовка к npm init
Не очень удобно при создании package.json при помощи npm init каждый раз вводить персональные данные. Чтобы этого избежать, сделаем настройку:
Внесем информацию об авторе "по умолчанию".
npm set init.author.name "$NAME"
npm set init.author.email "$EMAIL"
npm set init.author.url "$SITE"
Вместо переменных среды $NAME и тому подобных можно внести сами данные.
Данная команда выведет список всех возможных настроек.
npm config ls -l
Проверить, не устарели ли пакеты.
npm outdated
Фиксируем версии пакетов.
npm shrinkwrap
Прежде чем передавать продукт в промышленную эксплуатацию, по хорошему, нужно указать в package.json точные версии пакетов с которыми все 100% работает. Эта команда так и сделает. После ее выполнения в вашем package.json все версии пакетов будут жестко зафиксированы.
Обновление версии NPM.
npm update npm -g
четверг, 21 ноября 2013 г.
HTML parser на JavaScript
Напишем 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;
}
})();
Наша парсер будет проверять:
- отсутствие закрывающих тегов:
<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;
}
})();
среда, 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');
};
}
Подписаться на:
Сообщения (Atom)