Solving development problems  |  About this blog

Archive for the ‘javascript’ tag

Adding stored JavaScript procedures to MongoDB using Windows Batch script

MongoDB can use stored procedures similar to MSSQL. To make a MongoDB procedure create a new file, name it like DoSomething.js, write a function and attach it to database.

Guide through example

Lets say we have a log of website visitors stored in visitors collection inside myweb database. Each visitors record contains date and time of a visit. Now we want to create monthly statistics – how many users visits website per month.

visitors collection looks like:

> use myweb
> db.visitors.findOne()
{
  "_id": ObjectId("4e0cb7e7da3ea11d18d841e7"),
  "timestamp": "Thu Jun 30 2011 19:52:39 GMT+0200 (Central Europe Daylight Time)"
}

Most reusable way to get data is to create a stored procedure GetVisitsPerMonth() and call it on demand. Create a file GetVisitsPerMonth.js and copy-paste the following code inside. Note that file name is named by procedure.

function ()
{
	var result = db.visitors.group(
	{
		keyf: function(doc)
		{
			return { //NOTE: Bracket must be in this line!!!
				month: doc.timestamp.getMonth(),
				year: doc.timestamp.getFullYear()
			};
		},
		initial: {count:0},
		reduce: function(doc, prev) { prev.count++ }
	});

	return result;
}

How to attach one procedure

Stored procedure can be manually attached like this:

db = connect("localhost:27017/myweb");
db.system.js.save({"_id":"GetVisitsPerMonth", "value": function() { ... });

How to attach multiple procedures

Large sets of procedures should be well organized in files so you can quickly find a procedure and fix it if necessary. To insert or update all procedures at once use a simple batch script.

Create a new file, name it install.bat and copy-paste the following code inside:

@echo off

:parameters
set DBCON=localhost:27017/myweb
set MONGO=c:\mongodb\bin\mongo.exe
set SCRIPT=script.js

:startup
if not exist %MONGO% goto error
if not "%1"=="" goto add

rem Append connection string
echo Adding connection string...
echo db = connect("%DBCON%"); > %SCRIPT%
echo. >> %SCRIPT%

rem Append scripts
call %0 GetVisitsPerMonth
rem call %0 DoSomething
rem other procedures go here

goto install
rem goto end

:add
echo Adding script %1...
echo db.system.js.save({"_id":"%1", "value": >> %SCRIPT%
type %1.js >> %SCRIPT%
echo }); >> %SCRIPT%
echo. >> %SCRIPT%
goto end

:install
echo Running script...
%MONGO% %SCRIPT%
goto end

:error
echo Check if mongo is installed:
echo %MONGO%

:end

Edit parameters according to your needs: path to mongo.exe, database name, and list of procedures.
Simply run the script and it is done.

Testing a stored procedure

Log into mongo console:

> use myweb
> db.eval("GetVisitsPerMonth()")
[
  {
    "month": 5,
    "year": 2011,
    "count": 28233,
  },
  {
    "month": 6,
    "year": 2011,
    "count": 48026,
  },
  {
    "month": 7,
    "year": 2011,
    "count": 92754,
  }
]

Written by developer

October 7th, 2011 at 1:05 pm

JQuery problem with attr(“value”)

We experienced a problem using attr(“value”) JQuery function when selecting values that are not integers.

So if you have:

  <ul>
    <li id="li1" value="1">One</li>
    <li id="li2" value="2">Two</li>
    <li id="li3" value="3">Three</li>
    <li id="li4" value="4">Four</li>
  </ul>

It is working OK.

  $(function() {
    alert($('#li2').attr('value'));
  });

But if you have (string as values instead of integers):

  <ul>
    <li id="li1" value="One">One</li>
    <li id="li2" value="Two">Two</li>
    <li id="li3" value="Three">Three</li>
    <li id="li4" value="Four">Four</li>
  </ul>

This is not working.

  $(function() {
    alert($('#li2').attr('value'));
  });

Solution (rename value attribute to something else):

  <ul>
    <li id="li1" tag="One">One</li>
    <li id="li2" tag="Two">Two</li>
    <li id="li3" tag="Three">Three</li>
    <li id="li4" tag="Four">Four</li>
  </ul>

This is now OK.

  $(function() {
    alert($('#li2').attr('tag'));
  });

Conclusion:

Do not use reserved attributes as your custom attributes (where HTML element does not support them).

JavaScript alert (message) box alternative

If you want to quick test some functionality in JavaScript (fast clicking) then classic alert function is not a solution.
You can use this print function to simulate alert function. It writes message to a div, and if you want multiple divs just comment the code where existing div is reused.

Example od use:

print('your message', 'message box title');
function print(txt, title)
{
  txt = txt.replace(/\n/g,"<br />");
  if(!title) var title = "Message title";
  var template = "<center><div id='message_box' style='width:50%;"+
    "border:2px outset #ccc;background-color:#eee;'>"+
    "<div style='background-color:#5d5d92;color:#fff;font-weight:bold;"+
     "padding-left:5px;'>%TITLE%</div>"+
    "<div style='padding:15px;'>%TEXT%</div>"+
    "<center><input type='button' value='    OK    ' "+
    "onclick='document.getElementById(\"message_box\").style.display=\"none\"'"+
    " /></center>"+
    "</div></center>";

  var to_write = template.replace("%TITLE%", title);
  to_write = to_write.replace("%TEXT%", txt);

  if(document.getElementById("print_area"))
  { //If the 'print_area' exists, just write our data into it.
    document.getElementById("print_area").innerHTML = to_write;
  }
  else
  { //Else create the element before writing the data.
    var div = document.createElement("div");
    div.setAttribute("id","print_area");
    div.innerHTML = to_write;

    var body = document.getElementsByTagName("body")[0];
    	body.insertBefore(div,body.firstChild);
    //OR body.appendChild(div); //This will insert the message box at the end of the page.
  }
}

Preloading images with JavaScript (JQuery) and JQuery cool animations and effects

Model 1:

<img src="images/blank.gif" onload="replaceImage(this, 'your-image-url')" width="75" height="75" />

Use function to replaceImage:

function replaceImage(img, replacementImage)
{
  img.onload = null;
  img.src = replacementImage;
}

blank.gif is an 1px x 1px pixel transparent image. Basically, the idea is that this blank image is loaded and expanded to 75×75 (to preserve layout). That almost immediately fires the onload handler, which changes the image’s source to the desired image.

Model 2:

var img = new Image();
img.onError = function(){
  //error handling here
}
img.onLoad = function(){
  //success
containerForImg.removeClass('loading-image').append($(img));
}
img.onAbort = function(){
  //user clicked stop
}
img.src = "http://example.com"   //loading begins NOW!

References:

http://goo.by/wE3TFa
http://goo.by/w7AjZN
http://goo.by/w0CsbA

Background image animations:

$('#nav a')
.css( {backgroundPosition: "0 0"} )
.mouseover(function(){
$(this).stop().animate(
{backgroundPosition:"(0 -250px)"},
{duration:500})
})
.mouseout(function(){
$(this).stop().animate(
{backgroundPosition:"(0 0)"},
{duration:500})
})

Background transitions:

http://goo.by/wIJ6iD

Cool JQuery Animated robot:

TUTORIAL: http://goo.by/w4znKB
DEMO: http://goo.by/wmSMKs

Nice login page:

TUTORIAL: http://goo.by/wBrnQi
DEMO: http://goo.by/wdgmT1

Loading content:

TUTORIAL: http://goo.by/wn53Y0
DEMO: http://goo.by/wTGKDx

Use Ajax and JQuery to load dependable combobox values onchange event

Imagine that you have combobox for countries and you want to load all cities for chosen country in other dependable combobox.
This is the way how you can do it.

//This occurs when country combobox changes -> filling cities on country change
$('select#CountryId').change(function()
{
	url = 'http://www.someurl.com/' + $('select#CountryId').val();
	var dataToSend='CityId=' + $('select#CityId').val();
	$.ajax(
	{
		type: "POST",
		url: url,
		data: dataToSend,
		async : false,
		cache : false,
		dataType: "json",
		success:function(data)
		{
			var options = '';
			var selectedText = '';
			$.each(data, function(index, val)
			{
				var selectedString = '';
				if (val.Selected == true)
				{
					selectedText = val.Text;
					selectedString = ' selected="selected"';
				}
				options += '


';
			});
			$('select#CityId').html(options);
		}
	});
});

Written by Avivo

October 21st, 2010 at 5:49 pm