Archive for Javascript

Git Pre-commit hook

Yesterday I have written first code in Bash Script :D . It was a pre-commit hook which checks my Javascript files against bugs using JSLint. To install it, please download JSLint from http://www.javascriptlint.com/ and copy executable file (I renamed it from jsl to jslint) and config file to:

your_project/.git/hooks

and put following code into the pre-commit file which should be already there.

BTW, to skip it, do:

git commit --no-verify -m "Message"
#!/bin/sh
 
declare prev_file=""
declare prev_line=""
declare out
declare cmd=".git/hooks/jslint -conf .git/hooks/jslint.conf"
declare -i interrupt=0
 
if git-rev-parse --verify HEAD >/dev/null 2>&1; then
    against=HEAD
else
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
 
#take all changed files, example (1 modified file):
# Array[0]: M
# Array[1]: assets/css/styles.css
for FILE in `git diff --cached --name-status` ; do
	#if file exist
	if [ -e $FILE ]
		then
		#check only JS files
		if [[ $FILE == *.js ]]
			then
			#if file has been modified
			if [ "$prev_file" == "M" ]
				then
				cmd="$cmd -process $FILE"
			fi
		fi
	fi
 
	prev_file=$FILE;
done
 
#execute command and check if any mistakes has been made
out=$( eval "$cmd" );
 
#check if errors == 0 and warrnings == 0
if [ out ]
	then
	for LINE in $out; do
		if [ "$LINE" = "error(s)," ]
			then
			if [ "$prev_line" != "0" ]
				then
				interrupt=1
			fi
		fi
 
		if [ "$LINE" = "warning(s)" ]
			then
			if [ "$prev_line" != "0" ]
				then
				interrupt=1
			fi
		fi
 
		prev_line=$LINE;
	done
fi
 
echo "$out\n\n"
 
#display error messages and interrupt commit
if [ "$interrupt" == "1" ]
	then
	exit 1
fi
 
exit

Here is my config file:

#
# Configuration File for JavaScript Lint 0.3.0
# Developed by Matthias Miller (http://www.JavaScriptLint.com)
#
# This configuration file can be used to lint a collection of scripts, or to enable
# or disable warnings for scripts that are linted via the command line.
#
 
### Warnings
# Enable or disable warnings based on requirements.
# Use "+WarningName" to display or "-WarningName" to suppress.
#
+no_return_value              # function {0} does not always return a value
+duplicate_formal             # duplicate formal argument {0}
+equal_as_assign              # test for equality (==) mistyped as assignment (=)?{0}
+var_hides_arg                # variable {0} hides argument
+redeclared_var               # redeclaration of {0} {1}
+anon_no_return_value         # anonymous function does not always return a value
+missing_semicolon            # missing semicolon
+meaningless_block            # meaningless block; curly braces have no impact
+comma_separated_stmts        # multiple statements separated by commas (use semicolons?)
+unreachable_code             # unreachable code
+missing_break                # missing break statement
+missing_break_for_last_case  # missing break statement for last case in switch
+comparison_type_conv         # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
+inc_dec_within_stmt          # increment (++) and decrement (--) operators used as part of greater statement
+useless_void                 # use of the void type may be unnecessary (void is always undefined)
+multiple_plus_minus          # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
+use_of_label                 # use of label
+block_without_braces         # block statement without curly braces
+leading_decimal_point        # leading decimal point may indicate a number or an object member
+trailing_decimal_point       # trailing decimal point may indicate a number or an object member
+octal_number                 # leading zeros make an octal number
+nested_comment               # nested comment
+misplaced_regex              # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
+ambiguous_newline            # unexpected end of line; it is ambiguous whether these lines are part of the same statement
+empty_statement              # empty statement or extra semicolon
-missing_option_explicit      # the "option explicit" control comment is missing
+partial_option_explicit      # the "option explicit" control comment, if used, must be in the first script tag
+dup_option_explicit          # duplicate "option explicit" control comment
+useless_assign               # useless assignment
+ambiguous_nested_stmt        # block statements containing block statements should use curly braces to resolve ambiguity
+ambiguous_else_stmt          # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent)
+missing_default_case         # missing default case in switch statement
+duplicate_case_in_switch     # duplicate case in switch statements
+default_not_at_end           # the default case is not at the end of the switch statement
+legacy_cc_not_understood     # couldn't understand control comment using /*@keyword@*/ syntax
+jsl_cc_not_understood        # couldn't understand control comment using /*jsl:keyword*/ syntax
+useless_comparison           # useless comparison; comparing identical expressions
+with_statement               # with statement hides undeclared variables; use temporary variable instead
+trailing_comma_in_array      # extra comma is not recommended in array initializers
+assign_to_function_call      # assignment to a function call
+parseint_missing_radix       # parseInt missing radix parameter
 
 
### Output format
# Customize the format of the error message.
#    __FILE__ indicates current file path
#    __FILENAME__ indicates current file name
#    __LINE__ indicates current line
#    __ERROR__ indicates error message
#
# Visual Studio syntax (default):
+output-format ======================================================================\n__FILE__, line: __LINE__\n----------------------------------------------------------------------\n__ERROR__
# Alternative syntax:
#+output-format __FILE__:__LINE__: __ERROR__
 
 
### Context
# Show the in-line position of the error.
# Use "+context" to display or "-context" to suppress.
#
+context
 
 
### Semicolons
# By default, assignments of an anonymous function to a variable or
# property (such as a function prototype) must be followed by a semicolon.
#
+lambda_assign_requires_semicolon
 
 
### Control Comments
# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
# although legacy control comments are enabled by default for backward compatibility.
#
+legacy_control_comments
 
 
### JScript Function Extensions
# JScript allows member functions to be defined like this:
#     function MyObj() { /*constructor*/ }
#     function MyObj.prototype.go() { /*member function*/ }
#
# It also allows events to be attached like this:
#     function window::onload() { /*init page*/ }
#
# This is a Microsoft-only JavaScript extension. Enable this setting to allow them.
#
-jscript_function_extensions
 
 
### Defining identifiers
# By default, "option explicit" is enabled on a per-file basis.
# To enable this for all files, use "+always_use_option_explicit"
+always_use_option_explicit
 
# Define certain identifiers of which the lint is not aware.
# (Use this in conjunction with the "undeclared identifier" warning.)
#
# Common uses for webpages might be:
#+define window
#+define document
+define jQuery
+define Backbone
+define _
 
+define ModuleMap
+define ModuleWindows
+define ModuleData
 
### Files
# Specify which files to lint
# Use "+recurse" to enable recursion (disabled by default).
# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
# or "+process Folder\Path\*.htm".
#
#+process .git/hooks/jsl-test.js

I’m aware that my code just work, and can be improved, so feel free to post some suggestions.

Node.js – useful links

Written in polish
http://blog.end3r.com/129/node-js-czyli-serwer-w-javascript-zbior-przydatnych-linkow/

Modernizr

Use it if you need to detect browser features or add conditional script loading.
http://www.modernizr.com/

Drag & Drop in few lines of code

Implementing possiblity of moving elements on website seems to be hard for person with little experience with Javascript. Only thought is “i need to use some library”, it’s realy good idea… ? when we want add only one feature ? Probably not… I’ll show You 30 lines of code which does it.

Download files

View Example

Please open Your favourite text editor or other tool which You are using to write Your code and type:

Primary content of HTML file

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl" lang="pl">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Drag &amp; Drop</title>
    </head>
    <body>
 
    </body>
</html>

Between of BODY tags add DIV element (with its drag indicator) which will be moved.

<div>
    <div class="drag_indicator"></div>
</div>

In next order we are connecting style sheet and javascript file:

<link href="style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="dragdrop.js"></script>

Body of .css file:

div.drag_indicator {
    width            : 100px;
    height           : 20px;
    background-color : yellow;
}
 
#block1 {
    width            : 100px;
    height           : 100px;
    background-color : red;
    position         : absolute;
}

In this way we defined red block of 100×100 pixels dimension with absolute position relative to browser window and yellow block of 100×20 pixels dimension.

In file with .js extension we are writing code which catch 3 actions in document object:

  • onmousedown – when user press mouse button
  • onmousemove – when user move mouse cursor
  • onmouseup – when user release mouse button

Please look, mouse movement will be supported only when mouse button is pressed, that’s why onmousemove event is captured in onmousedown event.

document.onmousedown = function(e) {
    e = (e || event);
 
    document.onmousemove = function(e) {
        e = (e || event);          
    }
};
 
document.onmouseup = function(e) {
};

Code which supports events in Internet Explorer browser

e = (e || event);

When mouse button is pressed “onmousedown” event is activated. In first order we catch clicked object:

var target = e.target || e.srcElement;

If it is a HTML element, we break a code because this is highest embed element.

if (target.tagName == 'HTML')
        return;

In next step, we are looking for “drag_indicator” class in clicked element. If it haven’t, we are checking its parent nodes as long as we’ll found it or we came to the top of elements tree.

while (target != document.body && (target.className || "").indexOf("drag_indicator") == -1) {
    target = target.parentNode || target.parentElement;
}

If correct element haven’t been found, we break a script

if ((target.className || "").indexOf("drag_indicator") == -1)
        return;

When correct element has been clicked (div with block_indicator css class) script will go further. Because we wants to move entire block, not only a indicator we needs to find out its object. block_indicator is a child of block, so we needs to get it as follow:

target = target.parentNode;

In nexts lines we credit cursor position at the moment of click to sx and sy variables. In dx and dy we’ll keeping alteration (default is 0) and in l and t current position of block element.

 var sx = e.clientX, sy = e.clientY,
      dx = 0, dy = 0,
      l = target.offsetLeft,
      t = target.offsetTop;

In Firefox browser we encount a inconvenience of moving block. We can solve this problem using that condition:

if (e.preventDefault) {
    e.preventDefault();
}

In this moment we have all initial values. Now time has come for implementing dragging.

When block is moving, this function is called

document.onmousemove = function(e) {
}

To change a block position we need to read mouse cursor current position (e.clientX, e.clientY) and subtract it from initial values simultaneously saving it to dx i dy.

dx = e.clientX - sx;
dy = e.clientY - sy;

Finaly, we need to set current position (last position + alteration) to block element.

target.style.left = (l + dx) + "px";
target.style.top  = (t + dy) + "px";

When mouse button will be release, following code will be ran:

document.onmouseup = function(e) {
    document.onmousemove = null;
};

In this way, we created cross-browser compatible code that is smaller than 1kB.