вторник, 29 октября 2013 г.

Как запустить локально Grunt без установки grunt-cli?

Для того, чтобы запустить локально установленный Grunt JS без установки grunt-cli откройте командную консоль и перейдите в корневую папку вашего проекта:

cd C:\Work\myProject

После этого выполните в командной строке следующую команду:

node -e "require('grunt').cli();"

Таким образом вы запустите Grunt без grunt-cli.

How to automatically build the package.json file for Node.js projects?

Open command line tool (run cmd.exe).

Go to your project directory

cd C:\Work\MyProjectFolder

Excute command

npm init

to generate package.json file in your project directory.

Then use this simple script for Node JS to collect all dependencies for your project in ./node_modules folder:

var fs = require("fs");

function main() {
  fs.readdir("./node_modules", function (err, dirs) {
    if (err) {
      console.log(err);
      return;
    }
    dirs.forEach(function(dir){
      if (dir.indexOf(".") !== 0) {
        var packageJsonFile = "./node_modules/" + dir + "/package.json";
        if (fs.existsSync(packageJsonFile)) {
          fs.readFile(packageJsonFile, function (err, data) {
            if (err) {
              console.log(err);
            }
            else {
              var json = JSON.parse(data);
              console.log('"'+json.name+'": "' + json.version + '",');
            }
          });
        }
      }
    });

  });
}

main();

In my case, the above script outputs:

"colors": "0.6.0-1",
"commander": "1.0.5",
"htmlparser": "1.7.6",
"optimist": "0.3.5",
"progress": "0.1.0",
"request": "2.11.4",
"soupselect": "0.2.0", // Remember: remove the comma character in the last line.

Now, you can copy&paste this dependencies into your package.json file.

понедельник, 28 октября 2013 г.

Event-Focused JS and CSS

medium.com/objects-in-space/9b8a9dd7bfe3

The Rules

1. Use the data-behavior HTML attribute to designate what an element does when clicked, touched or interacted with in some way.

2. Visual changes happen via CSS only — size, color, opacity, and especially animation. Visual changes come about by adding or removing CSS classes via Javascript.

4. Use class names with semantic meaning for your document (.title, .actions, .error), not literal meaning (.big, .hide, .red).

5. Standard DOM events (click, change) are attached via .on() and in turn fire of meaningful custom events (open, save) via .trigger().

5. Responding to custom events is what adds and removes CSS class names to control visual changes.

6. When possible, events should be attached to the outer-most container of a “module”, not the actual item being interacted with.

Using data-behavior makes easy to add the behavior to new elements. If you have a dialog box that closes with a “Close” button and later decide to also add a big “X” up the top corner, give the “X” the same data-behavior attribute and it starts working the same as the “Close” button with no additional work—no need to add another class to a jQuery selector.

The standard syntax for this is $(‘[data-behavior~=open]’). This attaches to any HTML element that contains a data-behavior=”open” attribute. The ~= syntax means that “open” only needs to be somewhere in the data-behavior attribute. This means you can have multiple behaviors on one element.

If you attached two open events via a call to on() and later wanted to remove one with off(‘open’) you would be stuck: all open events are now gone. But calling off(‘open.task’) removes only that single event.

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <style type="text/css">
   
        body {
            background-color: #eeeeee;
        }
       
        h2 {
            margin: 0;
            font-size: 16px;
            font-weight: bold;
        }
       
        p {
            margin: 0;
        }
       
        ul {
            margin: 0;
            padding: 0;
            list-style: none;
        }
       
        form {
            text-align: center;
            overflow: hidden;
            height: 0;
            transition: height .5s;
        }
       
        /* Task default */
       
        ul#tasks li.task {
            overflow: auto;
            background-color: #ffffff;
            padding: 10px;
            transition: all 1s;
        }
       
        ul#tasks li.task a.open-task {
            display: block;
            float: right;
        }
       
        ul#tasks li.task a.close-task {
           display: none;
            float: right;
        }
       
        /* Task complete */
       
        ul#tasks li.task.complete {
            background-color: #dddddd;
            color: #999999;
        }
       
        ul#tasks li.task.complete a.open-task {
            display: none;
        }
       
        /* Task open */
       
        ul#tasks li.task.open form {
            height: 2em;
        }
       
        ul#tasks li.task.open a.close-task {
            display: block;
        }
       
        ul#tasks li.task.open a.open-task {
            display: none;
        }
       
    </style>
    <script data-main="js/index.js" src="js/lib/require/require.js"></script>
<title>Behavior</title>
</head>
<body>
    <ul id="tasks">
      <li class="task">
        <h2>Task name</h2>
        <p>Task description</h2>
        <a href="#" class="open-task" data-behavior="open">Open</a>
        <a href="#" class="close-task" data-behavior="close">Close</a>
        <form action="/echo/json/">
          <input type="text" name="name">
          <a href="#" class="save" data-behavior="save">Save</a>
        </form>
      </li>
    </ul>
</body>
</html>

index.js

require.config({
      paths: {
          jquery: 'lib/jquery/jquery'
      }
});

require(
    [
          'jquery'
    ]
    , function(
          $
    ) {

        // DOM Events - Стандартные события браузера

        // Разрешен клик только по элементу <a> с  data-behavior="open", находящемуся внутри <li class="task">
        $('ul#tasks li.task').on('click', '[data-behavior~=open]', function() {
           // Найти ближайший родительский тэг <li class="task"> и активировать приязанное ниже к нему событие 'open.task'
            $(this).closest('.task').trigger('open.task');
            return false; // Отменить стандартное поведение элемента <a> при клике по нему
        });

        // Разрешен клик только по элементу <a> с  data-behavior="close", находящемуся внутри <li class="task">
        $('ul#tasks li.task').on('click', '[data-behavior~=close]', function() {
            // Найти ближайший родительский тэг <li class="task"> и активировать приязанное ниже к нему событие 'close.task'
            $(this).closest('li.task').trigger('close.task');
            return false; // Отменить стандартное поведение элемента <a> при клике по нему
        });

        // Разрешен клик только по элементу <a> с  data-behavior="save", находящемуся внутри <li class="task">
        $('ul#tasks li.task').on('click', '[data-behavior~=save]', function() {
            // Найти ближайший родительский тэг <li class="task"> и активировать приязанное ниже к нему событие 'save.task'
            $(this).closest('li.task').trigger('save.task');
            return false; // Отменить стандартное поведение элемента <a> при клике по нему
        });

        // Custom Events - Новые собственные логические события, на которые теперь будут реагировать элементы,
        // созданные разработчиком и искуственно добавленные в систему

        // При возникновении события 'open.task'
        $('ul#tasks li.task').on('open.task', function() {
            $(this).addClass('open');
        });

        // При возникновении события 'close.task'
        $('ul#tasks li.task').on('close.task', function() {
            $(this).removeClass('open');
        });

        // При возникновении события 'save.task'
        $('ul#tasks li.task').on('save.task', function() {
            var $this = $(this);
            $.post('/echo/json').success(function(data) {
                $this.trigger('saved.task', data); // Активировать приязанное к тэгу <li class="task"> событие 'saved.task'
            });
        });

        // При возникновении события 'saved.task'
        $('ul#tasks li.task').on('saved.task', function(e, data) {
            $(this).trigger('complete.task'); // Активировать приязанное к тэгу <li class="task"> событие 'complete.task'
        });

        // При возникновении события 'complete.task'
        $('.task').on('complete.task', function() {
            $(this).trigger('close.task'); // Активировать приязанное к тэгу <li class="task"> событие 'close.task'
            $(this).addClass('complete');
        });
   
    }
);

воскресенье, 27 октября 2013 г.

Простая запись загрузки модулей в Require JS

define(function (require) {
    var dep1 = require('dep1'),
         dep2 = require('dep2'),
         dep3 = require('dep3'),
         dep4 = require('dep4'),
         dep5 = require('dep5'),
         dep6 = require('dep6'),
         dep7 = require('dep7');
         // ...

});

понедельник, 14 октября 2013 г.

File Uploads in Express

Handle File Uploads in Express (Node.js)
Posted on January 3rd, 2012 under Express.js, Node.js
Tags: Express, node.js
How to handle file uploads in Express Node.js framework
Handling file uploads was a pain in Node.js some time ago. Then came formidable. It was a good start but uploading files was still not easy. Then came connect-form which made things a little easier, based on which I wrote a tutorial on handling POST requests in Express.
In the rapidly progressing Node.js landscape, it wasn't going to be long before the crucial file uploading functionality would become actually very easy. The time has come now - handling file uploads in very easy now in Express.
You no more require any extra module to work with file uploads in Express. File upload now works right out of the Express box. Along with req.body, now you have req.files which contains the details about the files you have uploaded.
If you don't want to specify the upload directory, you are all set to start processing file uploads without any additional changes to app.js. The files are uploaded to "/tmp" by default. If you want to make sure the files are uploaded to a specific directory, you change this
app.use(express.bodyParser());
to
app.use(express.bodyParser({uploadDir:'./uploads'}));
The express.bodyParser config option can found on line 16 as of Express v2.5.4. In the above example, the files will be uploaded to a directory named "uploads" in the same dir as the Express app.
To get a better idea about how the file upload and POST submission can be handled, let's create a form:
<form method="post" enctype="multipart/form-data" action="/file-upload">
    <input type="text" name="username">
    <input type="password" name="password">
    <input type="file" name="thumbnail">
    <input type="submit">
</form>
To handle the form submission, let's create a route:
app.post('/file-upload', function(req, res, next) {
    console.log(req.body);
    console.log(req.files);
});
It's that simple now!
In the above example, we can access the file from the req.files.thumbnail object. The object name is dependent what you set the name attribute to in your HTML form. In our case we set it as "thumbnail", so we access it from req.files.thumbnail.
A file object has these commonly useful properties:
size - size of the file in bytes
path - the uploaded location of the file (files are renamed after uploading)
name - original name of the file (as on your hard disk)
type - the type of the file
To know the size of the file in our case: req.files.thumbnail.size
To know the original name of the file in our case: req.files.thumbnail.name
etc.
Note that the "uploads" directory is just the temporary upload directory; you will need to move the files to their respective intended locations once they are uploaded there. Following is an example of doing that. In this case we will be uploading the files to the "images" directory.
// we need the fs module for moving the uploaded files
var fs = require('fs');
app.post('/file-upload', function(req, res) {
    // get the temporary location of the file
    var tmp_path = req.files.thumbnail.path;
    // set where the file should actually exists - in this case it is in the "images" directory
    var target_path = './public/images/' + req.files.thumbnail.name;
    // move the file from the temporary location to the intended location
    fs.rename(tmp_path, target_path, function(err) {
        if (err) throw err;
        // delete the temporary file, so that the explicitly set temporary upload dir does not get filled with unwanted files
        fs.unlink(tmp_path, function() {
            if (err) throw err;
            res.send('File uploaded to: ' + target_path + ' - ' + req.files.thumbnail.size + ' bytes');
        });
    });
};
You can set the upload destination to anywhere in the OS where you have access to. In case you want the files to be publicly available they should be uploaded within the "public" directory of Express. If want to upload the files to any sub-directory with "public", make sure they already exist else you will run into "Error: ENOENT, no such file" or directory error.
That's it! Handling file uploads in very easy in Express now. In case your old app is not handling file uploads as expected, try updating to the latest version of Express and NPM, and re-create the app. Any queries, suggestions; ping me in the comments.

To handle multiple file uploading use the multiple attribute. This gives you the ability to select more than one file at a time.
label(for=’fileSelect’) Load File
input(type=’file’, name=’fileSelect’, multiple=’multiple’)
In your app.js, loop through your selected files:
var len = req.files.fileSelect.length;
for(var i = 0; i < len; i++) {
console.log(req.files.fileSelect[i]);
}

пятница, 4 октября 2013 г.

Generating random numbers in Javascript in a specific range

/**
 * Returns a random number between min and max
 */
function getRandomArbitary (min, max) {
    return Math.random() * (max - min) + min;
}

/**
 * Returns a random integer between min and max
 * Using Math.round() will give you a non-uniform distribution!
 */
function getRandomInt (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
Here's the logic behind it. It's a simple rule of three:

Math.random() returns a Number between 0 (inclusive) and 1 (exclusive). So we have an interval like this:

[0 .................................... 1)
Now, we'd like a number between min (inclusive) and max (exclusive):

[0 .................................... 1)
[min .................................. max)
We can use the Math.random to get the correspondent in the [min, max) interval. But, first we should factor a little bit the problem by subtracting min from the second interval:

[0 .................................... 1)
[min - min ............................ max - min)
This gives:

[0 .................................... 1)
[0 .................................... max - min)
We may now apply Math.random and then calculate the correspondent. Let's choose a random number:

                Math.random()
                    |
[0 .................................... 1)
[0 .................................... max - min)
                    |
                    x (what we need)
So, in order to find x, we would do:

x = Math.random() * (max - min);
Don't forget to add min back, so that we get a number in the [min, max) interval:

x = Math.random() * (max - min) + min;
That was the first function from MDC. The second one, returns an integer between min and max, both inclusive.

Now for getting integers, you could use round, ceil or floor.

You could use Math.round(Math.random() * (max - min)) + min, this however gives a non-even distribution. Both, min and max only have approximately half the chance to roll:

min...min+0.5...min+1...min+1.5   ...    max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘   ← round()
   min          min+1                          max
With max excluded from the interval, it has an even less chance to roll than min.

With Math.floor(Math.random() * (max - min +1)) + min you have a perfectly even distribution.

min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
|        |        |         |        |        |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘   ← floor()
   min     min+1               max-1    max
You can't use ceil() and -1 in that equation because max now had a slightly less chance to roll, but you can roll the (unwanted) min-1 result too.

Generating random numbers in Javascript in a specific range

/**
 * Returns a random number between min and max
 */
function getRandomArbitary (min, max) {
    return Math.random() * (max - min) + min;
}

/**
 * Returns a random integer between min and max
 * Using Math.round() will give you a non-uniform distribution!
 */
function getRandomInt (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
Here's the logic behind it. It's a simple rule of three:

Math.random() returns a Number between 0 (inclusive) and 1 (exclusive). So we have an interval like this:

[0 .................................... 1)
Now, we'd like a number between min (inclusive) and max (exclusive):

[0 .................................... 1)
[min .................................. max)
We can use the Math.random to get the correspondent in the [min, max) interval. But, first we should factor a little bit the problem by subtracting min from the second interval:

[0 .................................... 1)
[min - min ............................ max - min)
This gives:

[0 .................................... 1)
[0 .................................... max - min)
We may now apply Math.random and then calculate the correspondent. Let's choose a random number:

                Math.random()
                    |
[0 .................................... 1)
[0 .................................... max - min)
                    |
                    x (what we need)
So, in order to find x, we would do:

x = Math.random() * (max - min);
Don't forget to add min back, so that we get a number in the [min, max) interval:

x = Math.random() * (max - min) + min;
That was the first function from MDC. The second one, returns an integer between min and max, both inclusive.

Now for getting integers, you could use round, ceil or floor.

You could use Math.round(Math.random() * (max - min)) + min, this however gives a non-even distribution. Both, min and max only have approximately half the chance to roll:

min...min+0.5...min+1...min+1.5   ...    max-0.5....max
└───┬───┘└────────┬───────┘└───── ... ─────┘└───┬──┘   ← round()
   min          min+1                          max
With max excluded from the interval, it has an even less chance to roll than min.

With Math.floor(Math.random() * (max - min +1)) + min you have a perfectly even distribution.

min.... min+1... min+2 ... max-1... max.... max+1 (is excluded from interval)
|        |        |         |        |        |
└───┬───┘└───┬───┘└─── ... ┘└───┬───┘└───┬───┘   ← floor()
   min     min+1               max-1    max
You can't use ceil() and -1 in that equation because max now had a slightly less chance to roll, but you can roll the (unwanted) min-1 result too.

четверг, 3 октября 2013 г.

From Node to Connect and Express Server

Node HTTP server

// Require what we need
var http = require("http");

// Build the server
var app = http.createServer(function(request, response) {
  response.writeHead(200, {
    "Content-Type": "text/plain"
  });
  response.end("Hello world!\n");
});

// Start that server, baby
app.listen(1337, "localhost");
console.log("Server running at http://localhost:1337/");

В браузере набрать
localhost:1337
localhost:1337/anime_currency
localhost:1337/?onlyfriend=anime

------------------------------------------------------------------------------

The request handler

var app = http.createServer(function(request, response) {

  // Build the answer
  var answer = "";
  answer += "Request URL: " + request.url + "\n";
  answer += "Request type: " + request.method + "\n";
  answer += "Request headers: " + JSON.stringify(request.headers) + "\n";

  // Send answer
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end(answer);

});


------------------------------------------------------------------------------

Add 404 Error page

var http = require("http");

http.createServer(function(req, res) {

  // Homepage
  if (req.url == "/") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("Welcome to the homepage!");
  }

  // About page
  else if (req.url == "/about") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("Welcome to the about page!");
  }

  // 404'd!
  else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("404 error! File not found.");
  }

}).listen(1337, "localhost");

------------------------------------------------------------------------------

Connect

// Require the stuff we need
var connect = require("connect");
var http = require("http");

// Build the app
var app = connect();

// Add some middleware
app.use(function(request, response) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Hello world!\n");
});

// Start it up!
http.createServer(app).listen(1337);

------------------------------------------------------------------------------

Connect with Middleware

var connect = require("connect");
var http = require("http");
var app = connect();

// Logging middleware
app.use(function(request, response, next) {
  console.log("In comes a " + request.method + " to " + request.url);
  next();
});

// Send "hello world"
app.use(function(request, response) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Hello world!\n");
});

http.createServer(app).listen(1337);

------------------------------------------------------------------------------

Connect with Logger

var connect = require("connect");
var http = require("http");
var app = connect();

app.use(connect.logger());
// Fun fact: connect.logger() returns a function.

app.use(function(request, response) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Hello world!\n");
});

http.createServer(app).listen(1337);

Перейти в браузере на localhost:1337

------------------------------------------------------------------------------

Many pages with Connect

var connect = require("connect");
var http = require("http");
var app = connect();

app.use(connect.logger());

// Homepage
app.use(function(request, response, next) {
  if (request.url == "/") {
    response.writeHead(200, { "Content-Type": "text/plain" });
    response.end("Welcome to the homepage!\n");
    // The middleware stops here.
  } else {
    next();
  }
});

// About page
app.use(function(request, response, next) {
  if (request.url == "/about") {
    response.writeHead(200, { "Content-Type": "text/plain" });
    response.end("Welcome to the about page!\n");
    // The middleware stops here.
  } else {
    next();
  }
});

// 404'd!
app.use(function(request, response) {
  response.writeHead(404, { "Content-Type": "text/plain" });
  response.end("404 error!\n");
});

http.createServer(app).listen(1337);

------------------------------------------------------------------------------

Express

var express = require("express");
var http = require("http");
var app = express();
http.createServer(app).listen(1337);

------------------------------------------------------------------------------

Express Routing

var express = require("express");
var http = require("http");
var app = express();

app.all("*", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  next();
});

app.get("/", function(request, response) {
  response.end("Welcome to the homepage!");
});

app.get("/about", function(request, response) {
  response.end("Welcome to the about page!");
});

app.get("/hello/:who", function(req, res) {
  res.end("Hello, " + req.params.who + ".");
  // Fun fact: this has security issues
});

app.get("*", function(request, response) {
  response.end("404!");
});

http.createServer(app).listen(1337);

Перейти в браузере на localhost:1337/hello/animelover69

------------------------------------------------------------------------------

Express Redirect

response.redirect("/hello/anime");
response.redirect("http://www.myanimelist.net");
response.redirect(301, "http://www.anime.org");  // HTTP status code 301

response.sendFile("/path/to/anime.mp4");

------------------------------------------------------------------------------

Express View

// Start Express
var express = require("express");
var app = express();

// Set the view directory to /views
app.set("views", __dirname + "/views");

// Let's use the Jade templating language
app.set("view engine", "jade");

app.get("/", function(request, response) {
  response.render("index", { message: "I love anime" });
});

------------------------------------------------------------------------------

Connect and Node inside Express

var express = require("express");
var app = express();

app.use(express.logger());  // Inherited from Connect

app.get("/", function(req, res) {
  res.send("anime");
});

app.listen(1337);

------------------------------------------------------------------------------

Read more on evanhahn.com/understanding-express-js/

JavaScript without jQuery

Selectors

Selecting by ID:

$('#foo')
document.getElementById('foo')

Selecting by class (not compatible with IE6-8, but good with everything else):

$('.bar')
document.getElementsByClassName('bar')

Selecting by tag name:

$('span')
document.getElementsByTagName('span')

Selecting sub-elements:

$('#foo span')
document.getElementById('foo').getElementsByTagName('span')

Selecting "special" elements:

$('html')
document.documentElement

$('head')
document.head

$('body')
document.body

Attributes

Getting/setting HTML:

$('#foo').html()
document.getElementById('foo').innerHTML

$('#foo').html('Hello, world!')
document.getElementById('foo').innerHTML = 'Hello, world!'

Dealing with classes:

$('#foo').addClass('bar')
document.getElementById('foo').className += ' bar '

$('#foo').removeClass('bar')
document.getElementById('foo').className = document.getElementById('foo').className.replace(/bar/gi, '')

$('#foo').hasClass('bar')
document.getElementById('foo').className.indexOf('bar') !== -1

Getting an input's value:

$('#foo').val()
document.getElementById('foo').value

Effects

Showing and hiding:

$('#foo').show()
document.getElementById('foo').style.display = ''

$('#foo').hide()
document.getElementById('foo').style.display = 'none'

Changing CSS:

$('#foo').css('background-color', 'red')
document.getElementById('foo').style.backgroundColor = 'red'

Events

Document ready

Do it the way MDN does it:

document.onreadystatechange = function() {
    if (document.readyState === 'complete') {
        // DOM is ready!
    }
};

Second, use domReady, a tiny library that's used like this:

domready(function() {
    // DOM is ready!
});

Clicks

$('#foo').click(function() { ... })
document.getElementById('foo').onclick = function() { ... }

Parsing JSON:

jQuery.parseJSON(json)
JSON.parse(json)
// The JSON object isn't in older browsers, so you can include it if it's not there.
// http://github.com/douglascrockford/JSON-js/blob/master/json2.js