Let’s write our first “Hello MongoDB” with NodeJS

As you have seen recently, i’m not writing so much here, because i’m working on new technologies like NodeJS and MongoDB. Fascinated by this next-gen “AMP”, i’m trying to write my first scripts trying to make such a basic job: get some data from somewhere, save them into the DB then next, and finally, get all the items that are in our “table” (i’ll explain you later why i’m writing it like this).

First of all, i have to explain you something before we can start. MongoDB, as you may know, is a next generation NO-SQL in-mind to store data in it. But, just like SQL Databases, they do not use “tables” as we do not have to declare table columns or something like that. We have collections defined by models. Something like virtual “tables” where:

  • Collection = list of “tables”
  • Models =  column of the tables (with flexible data type: integers, strings, arrays or objects)

So, when you’ll catch this knowledge, you’ll be ready to start this simple tutorial. What we will need to accomplish this are:

  • NWM (made by me, a simple “WAMP-like”, ready-to-use with NodeJS and MongoDB)
  • MongoVue (to inspect the database, just like PhpMyAdmin, feel free to use the one you prefer, optional)
  • MongoDB instance running (run your “start MongoDB” script inside NWM, usually located at Start -> All Programs -> NWM -> Start MongoDB).

Obviously, i’m not going to explain how to install them, basically you have to download them, install and you’re done. For MongoDB will be used the “test” database, automatically created for default setups.

Prepare our database

Open your Command Prompt (until now, it will be called CMD) and write these command

mongo [Enter]
use test [Enter]
> db.addUser("test","test"); [Enter]
exit [Enter]

what we have done here, was adding a user for the connection (actually not really required, as we’re in a testing environment) and initializing the database.

Install “mongoose” for NodeJS

Just like PHP, also NodeJS (let’s think about it, like an Apache+PHP) does not have a native library to connect to MongoDB, so we have to install it by ourself. So open your CMD and type this command

npm install mongoose

and wait until it finishes.

Run our script

Yes, it’s not just like Apache that you have a “www” directory where you put your scripts and then point to your “localhost” and you can see them. In NodeJS you have to run your script by typing this in your CMD

node your_script.js

So what what we have to do now is copying this script

/*
	Author: Julian Xhokaxhiu
	Version: Do you really think it is necessary?
	Description: This is a simple "Hello MongoDB" to set and get data, to/from database.
*/

/*
	@http: Our HTTP library so we can output a response to the browser
*/
var http = require('http');
/*
	@mongoose: Our DB library, so we can get/set our data from/to database
*/
var mongoose = require('mongoose');
/*
	@Schema: basic schema to store data into the database.
	@Mixed: object datatype to store our simple object in the database
*/
var Schema = mongoose.Schema, Mixed = Schema.Types.Mixed;
/*
	@RequestObject: this will be our "template" to store the data into our collection
*/
var RequestObject = new Schema({
	id: Number, //int
	req: Mixed //object
});
// let's connect to the database...
mongoose.connect('mongodb://localhost/test');
// tell mongoose that this is our template so we can request it later
mongoose.model('RequestObject',RequestObject);

// @i: simple counter
var i = 0;
// create our server listener @ "http://localhost:80"
http.createServer(function(req,res){
	// tell the browser that this we will output JSON
	res.writeHead(200,{'Content-Type':'application/json'});

	// get the template so we can build an object to store, or retrive data later
	var ro = mongoose.model('RequestObject');
	// create an object store
	var tmp = new ro();
	// setting values into it...
	tmp.id = i;
	tmp.req = {
		'headers':req.headers,
		'httpVersion':req.httpVersion,
		'method':req.method,
		'trailers':req.trailers,
		'url':req.url
	};
	// ...and saving into the database
	tmp.save(function(err){
		// if something bad happens, tell the error
		if(err)res.write('{"Save Error":"'+err.err+'"}');
	});
	ro.find({},function(err,objs){
		// if something bad happens, tell the error
		if(err)res.write('{"Find Error":"'+err.err+'"}');
		else{
			// Get our data from the collection store
			res.write(JSON.stringify(objs));
			// Finally, close the HTTP response, we're done.
			res.end();
		}
	});

	// increase our simple counter...
	i++;
}).listen(80,'127.0.0.1');
// Just tell the console that we're running at this address, purely informative
console.log('Server running at http://127.0.0.1:80/');

and save it to our preferred location (the Desktop will be fine) and call it “test.js“.

Then finally, to test it, just type

cd Desktop
node test.js

and point your browser at http://localhost/ so you can view your data.

Congratulations!

You made it! You’re runnig the “Hello MongoDB” script and you’re pushing data into the database. After this, you’re getting ALL the data inside the database.

So, what will MongoVUE will be need for? Well, if you’re curious to know how the data is inside (let’s think about PHPMyAdmin ) and you want to view/edit/delete data into, you can do that. I’m NOT going to explain how to use it, think about this as a reference. You can find more Admin UIs for MongoDB at http://www.mongodb.org/display/DOCS/Admin+UIs

Comment below if you find any mistake or you have any suggestion regarding this article.
So, thanks for reading, i hope you’ll enjoy this.

[UPDATED] JonDesign’s SmoothGallery Shadowbox plugin

Hi there,

very little and fast post about this plugin. Today i had to make SmoothGallery to work with Shadowbox plugin (rather than Lightbox) and within an extreme surprise i didn’t found nothing on the web. So i had to do it myself. I thought it would be much difficult, but in fact it was really easy to fix it. So here, i’m sharing this with you, if you had also my problem.

To make it work just replace at line 116

this.currentLink.rel = 'lightbox';

with

this.currentLink.addEvent('click',function(e){
 Shadowbox.open({
  content:e.target.href,
  player:'img',
  title:e.target.title
 });
 e.preventDefault()
});

and that’s it!

This method works with JonDesign’s SmoothGallery v2.0 + Shadowbox 3.1.3. Maybe it will work also with next versions of each other, but i don’t know, we will see it.

Hope you’ll enjoy this post :)

UPDATE!
Found the method to make it work also on v2.1! (useful if you use jQuery and MooTools, all together).

Replace lines 133-136

this.currentLink = new Element('a').addClass('open').setProperties({
 href: '#',
 title: ''
}).injectInside(element);

with

this.currentLink = new Element('a').addClass('open').setProperties({
 href: '#',
 title: ''
}).injectInside(element).addEvent('click',function(e){
 Shadowbox.open({
  content:e.target.href,
  player:'img',
  title:e.target.title
 });
 e.preventDefault()
});

That’s it! Enjoy :)

[UPDATED] How to fix npm PATH of NWM

Hello everyone again,

just in a hurry, i’m here to tell how to fix npm PATH of NWM 0.1b, i knew that something was missing (didn’t notice why that final b? that means BETA) and finally someone reported it to me (thanks Autarc). I’ve found a faster bugfix for this:

1) Add “global=true” to your “npmrc” file (located at “nodejs\node_modules\npm” of your NWM installation folder)
2) Add “%USERPROFILE%\AppData\Roaming\npm” to your PATH variabile

“global=true” is necessary because it make the installation of packages always global (with a fixed path) also if you do not use the “-g” parameter. That’s why the PATH can be easily set fixed and always work.

That’s it. Restart your machine and you’re done. This will be fixed with the new NWM 0.2b release, with a little add-in inside (didn’t you ever heard of Mongoose?).

Happy new year and see you with the next release of NWM! :)

UPDATE: as promised, enjoy the new release 0.2b of NWM! (Mongoose is not included in this release, will come in next one)

Little christmas surprise for you: NWM!

Long time no see duh? Well, i didn’t forget you, neither i didn’t stop developing cool new things. In fact you can notice that right of this post, you can find some new links about me, followed by my new GitHub channel. But today i’m not going to talk to you abuot this, but about my latest work, which is called NWM.

What is NWM? NWM stay for “NodeJS (for) Windows (with) MongoDB”, and it’s a brend new like LAMP-like technology to developer in your local machine, without having to pull or push some code on the cloud, but right to your foldder, point to you “localhost” and you’re done! In fact, you will know that what i’m saying is not truly, but in part, it is. This because, NodeJS is only a server-side technology that can offer to you, more like than Apache + PHP can offer to you, powerful, faster, lighter and cleaner. No more headaches!

But what does make NWM better than downloading NodeJS and MongoDB by themselfs? Well, recently i’m going to start a brand new project, which i’ll talk to you at a later time, based upon NodeJS and MongoDB, so, about this, i started to search the internet about a LAMP-like software, which does have NodeJS and MongoDB…but i didn’t find anything, only a post talking about NUM, from Travis Glines, which ispired me in fact, about making a Windows based like.

This way was borned NWM! This is only the first release, don’t expect something magic, this will only automatically set up for you a clean installation of MongoDB, NodeJS + Path autoset to use them on your CMD (like npm). But later, i’ll try to make a more easier GUI for basic users, fullfilled with all the necessary options for the Advanced users, and letting to tweak their custom installation for nerds one.

So, i hope you’ll enjoy this release. Stay tuned for more updates about this project. I hope you’ll be happy about this little christmas gift that i’ve made to you :)

Grooveshark Mediakeys Reloaded

Hi there,

just want to share with you my new work that i have made. I’m talking about a Google Chrome extensions that is called Grooveshark Mediakeys Reloaded.

What you can do with it? Simple! It allows you to use your Keyboard Multimedia Keys with Grooveshark, from the tab itself, from the other tabs but also if Grooveshark is in a separeted window (like a Web App). So you will forget forever about Grooveshark as a Web App with those click limitations. Just use your multimedia keys like you just already do with iTunes or Windows Media Player.

Where you can download this? Just point your browser here: https://chrome.google.com/extensions/detail/jaicdopiaemahiofpemoljckbebcpcjm

Like i always, i post my code snippet. This time i sugget you to download it and see it from there, just because i can also update it, so you will always see the code evolving from there. If you’re a developer, just like me, you know how to do it ;)

Thanks for reading, i hope you’ll enjoy this.

ContentFlow Shadowbox plugin

Hi there,

for all you guys who knows already what ContentFlow is and what ShadowBox is, this is for you! (but this is also for you, if you don’t know what they are,  just visit their respective websites). What does it do? Simply, inspired by the LightBox plugin, this does just the same, but with Shadowbox, so when you click on an active item and on it’s image link, it just open it on Shadowbox without leaving your page. And what about the title? Yeah it does get it also.

Remember to include this script AFTER ContentFlow, which it have to be AFTER Shadowbox (otherwise it won’t work).

Enjoy!

/*  ContentFlowAddOn_shadowbox, version 0.1
 *  (c) 2011 Julian Xhokaxhiu
 *
 */
new ContentFlowAddOn('shadowbox',{
	ContentFlowConf:{
        onclickActiveItem: function (item){
            if (item.content.getAttribute('src'))
		Shadowbox.open({
			player:'img',
			title:(item.caption)?item.caption.firstChild.nodeValue.replace(/]*>/,': '):'',
			content:item.content.getAttribute('src')
		});
        }
    }
});

jQuery Storyline

Hi all, once again :)

No much time has passed duh? Well in fact i was working on a nice plugin there (once again) which i would like to share with you, which is called jQuery Storyline. So now you will ask, what is this? It’s just an easy plugin for creating a custom, and nice, storyline board with just a little effort than a string of code. How? Simply, just with the power of jQuery.

So what you can do with that? Anything that you like, from a custom date slideshow to your preferred characters hero sheet. Oh and i forgot to tell you that you can put anything you want inside the boxes, making them custom for really, so links or actions are decided by you!

And finally, last but not least, it has Touch feature inside prepacked. What does it mean? That you don’t need to bother with them, it just works. Put your finger over it, and scroll it like a page :)

Thanks for reading and i hope you’ll enjoy this.

CSS

/** jQuery Storyline Plugin **/
.ui-storyline{height:500px;border:1px solid black}
.ui-storyline .scroll-content-item{height:100%;display:block;float:left}
/* ScrollBar UI */
.scroll-pane{overflow:auto}
.scroll-bar-wrap{clear:left;padding:0;margin:0 auto}
.scroll-bar-wrap .ui-slider{background:none;border:0;height:2em;margin:0 auto}
.scroll-bar-wrap .ui-handle-helper-parent{position:relative;width:100%;height:100%;margin:0 auto}
.scroll-bar-wrap .ui-slider-handle{top:.2em;height:1.5em}
.scroll-bar-wrap .ui-slider-handle .ui-icon{margin:-8px auto 0;position:relative;top:50%}

JS

/*
	jQuery Plugin StoryLine

	This plugin was made with a particular function of adding multiple inputs with autocomplete with some other functions like adding a new item to the list that is not into
	the autocomplete list or removing an item (when there are more than one into a list).

	Author: Julian Xhokaxhiu
	Date: 07/01/2010
	Changelog:
		- 0.1:  initial relase version.
*/
var StoryLine;
(function($){
	StoryLine = function(options){
		var father = $(this).parent();
		var scrollBar;
		var scrollContent = $(this);
		var scrollPane;
		var handleHelper;
		var settings = $(this).data('storylinesettings');
		// If settings are not declared, we set the default value for them
		if(!settings){
			settings = {
				elementwidth: 0,
				elementsperview: 0,
				tonextonslide: 100,
				tonextonclick: 200,
				step: 1,
				touchstep: 2
			}
		};
		// Private methods - can only be called from within this object
		var IntFunz = {
			//size scrollbar and handle proportionally to scroll distance
			sizeScrollbar:function(){
				var remainder = scrollContent.width()-scrollPane.width();
				var proportion = remainder/scrollContent.width();
				var handleSize = scrollPane.width()-(proportion*scrollPane.width());
				scrollBar.find(".ui-slider-handle").css({width:handleSize,"margin-left":-handleSize/2});
				handleHelper.width("").width(scrollBar.width()-handleSize);
			},
			//reset slider value based on scroll content position
			resetValue:function(){
				var remainder = scrollPane.width()-scrollContent.width();
				var leftVal = scrollContent.css("margin-left")==="auto"?0:parseInt(scrollContent.css("margin-left"));
				var percentage = Math.round(leftVal/remainder*100);
				scrollBar.slider("value",percentage);
			}
		};

		if(typeof(options)=='string'){
			if(IntFunz[options])IntFunz[options].apply(null,Array.prototype.slice.call(arguments,1));
		}else if(options){
			settings = $.extend(settings, options || {});
			$(this).data('storylinesettings',settings);

			// Do this only if the control is not already binded
			if (!scrollContent.parent().hasClass('ui-storyline')){
				// Get the number of the elements
				var elementsnumber = scrollContent.children().length;
				scrollBar = $('</pre>
<div>',{'class':'scroll-bar'}); scrollPane = $('
<div>',{'class':'ui-storyline scroll-pane'}) // Append HTML scrollContent.width(elementsnumber*settings.elementwidth).children().each(function(i,v){$(v).addClass('box scroll-content-item').width(settings.elementwidth).touchwipe({wipeLeft:function(){scrollBar.slider('value',scrollBar.slider('value')+settings.touchstep)},wipeRight:function(){scrollBar.slider('value',scrollBar.slider('value')-settings.touchstep)},preventDefaultEvents:false})}); scrollPane = scrollContent.addClass('scroll-content').wrap(scrollPane).parent(); father.width(settings.elementwidth*settings.elementsperview).append($('
<div>',{'class':'scroll-bar-wrap ui-widget-content ui-corner-bottom'}).width(settings.elementwidth*settings.elementsperview).touchwipe({wipeLeft:function(){scrollBar.slider('value',scrollBar.slider('value')-settings.touchstep)},wipeRight:function(){scrollBar.slider('value',scrollBar.slider('value')+settings.touchstep)},preventDefaultEvents:false}).append(scrollBar)).append($('
<ul>',{'class':'ui-storyline-command-buttons'}).append($('
	<li>').append($('<button>',{'id':'prev'}).text('Prev').button().click(function(){scrollBar.slider('value',scrollBar.slider('value')-settings.step)}))).append($('</button></li>
	<li>').append($('</li>
	<li><button></button><button>',{'id':'next'}).text('Next').button().click(function(){scrollBar.slider('value',scrollBar.slider('value')+settings.step)})))); // Make it a slider scrollBar.slider({ slide:function(event,ui){ if(ui.value)scrollContent.animate({"margin-left":ui.value*(-settings.elementwidth)},settings.tonextonslide); else scrollContent.animate({"margin-left":0},settings.tonextonslide); }, change:function(event,ui){ if(ui.value)scrollContent.animate({"margin-left":ui.value*(-settings.elementwidth)},settings.tonextonclick); else scrollContent.animate({"margin-left":0},settings.tonextonclick); }, min:0, max:(elementsnumber-settings.elementsperview), step:settings.step, animate:true }); //append icon to handle handleHelper = scrollBar.find(".ui-slider-handle").mousedown(function(){scrollBar.width(handleHelper.width())}).mouseup(function(){scrollBar.width("100%")}).append($('<span><span>',{'class':'ui-icon ui-icon-grip-dotted-vertical'})).wrap($('</span></span>
<div>',{'class':'ui-handle-helper-parent'})).parent(); //change overflow to hidden now that slider handles the scrolling scrollPane.css("overflow","hidden"); // Resize and position correctly the scrollbar on window resize $(window).resize(function(){ IntFunz.resetValue(); IntFunz.sizeScrollbar(); }); //safari wants a timeout setTimeout(function(){ IntFunz.sizeScrollbar(); },10); } } }; $.fn.extend({ storyline:function(){ var args = arguments; this.each(function(){StoryLine.apply(this,args)}); } }); })(jQuery);

AutoComplete Extended

Hi there,

so much time have been passed since the last time, didn’t it? Well, you may think that this blog is already dead and nothing is going on, but in fact i’ve been so much busy in this time that you cannot mind it :) But this isn’t the topic of this post so maybe, i’ll talk about this the next one (maybe, i want to focus only on ready-to-use scripts).

So since the last time, i’ve talked to you about a plugin i was writing for jQuery and jQueryUi, which it’s name was “AutoComplete Extended”…well, now it’s ready to use and fully working, tested and bug-free (i hope). But before jumping to the code and also the webpage i’ve prepared for it, i would like to talk about what can it do for you.

Well, maybe you’re thinking that this plugin is another “boring” one, maybe with two more options and nothing important inside, but believe me, you’re totally wrong and i can explain why. Let’s imagine a scenario where you would like to tag an article with multiple tags,  just the way you do here in WordPress. Well, there are two options to do so: a multiple selection of combobox or a (lucky?) type of tags just like they’re on your DB. But from now on you can have a third option: a multiple set of inputs preloaded with the Autocomplete option (which seek into your actually list of tags into yuor DB).

So, maybe you’re in this scenario, or maybe not, but if you’re curious to know what is, and how it works this plugin just go here. If you just want the code, see at the bottom of this page (but if you’re looking for the most updated version, visit the dedicated webpage).

I would like also to thank my best friend Emanuele Minotto who helped me on setting up, some little things, in the new subdomain “examples” that i’ve created just for putting up my scripts and so on.

I hope you will enjoy it, as much as i did :)

Thanks for reading again, and happy new year!

/*
	jQuery Plugin AutoCompleteExtended

	This plugin was made with a particular function of adding multiple inputs with autocomplete with some other functions like adding a new item to the list that is not into
	the autocomplete list or removing an item (when there are more than one into a list).

	Author: Julian Xhokaxhiu
	Date: 16/06/2010
	Changelog:
		- 0.2:  completely rewrited in object-oriented method. Now every object could be an instance of this plugin,
				so you can call methods after you have initialized it.
		- 0.1:  initial relase version.
*/
var AutocompleteExtended;
(function ($) {
	AutocompleteExtended = function(options){
		var input = $(this);
		// Leggo i valori cachati nell'oggetto
		var cache = $(this).data('autocompleteextendedcache');
		if(!cache)cache={};
		var father = $(this).data('autocompleteextendedfather');
		var theInput = $(this).data('autocompleteextendedoriginalinput');
		var count = $(this).data('autocompleteextendedcount');
		if(!count)count = 0;
		var settings = $(this).data('autocompleteextendedsettings');
		// If settings are not declared, we set the default value for them
		if(!settings){
			settings = {
				SearchUri: '',
				minLength: 2,
				useRegEx: false,
				RegExPattern: null,
				multiple: false,
				defaultvalue: null,
				withList: true,
				withNew: true,
				callback: null,
				buttons: {
					useIcons: true,
					textNew: 'New',
					textList: 'List',
					textDelete: 'Delete'
				},
				windows: {
					add: {
						onOK: null,
						onCancel: null,
						onClose: null
					},
					list: {
						onOK: null,
						onCancel: null,
						onClose: null
					},
					remove: {
						onOK: null,
						onCancel: null,
						onClose: null
					}
				}
			}
		};
		// Private methods - can only be called from within this object
		var IntFunz = {
			addActionButton: function (options) {
				if(!father.find('.autocompleteextended-buttoncontainer').length)father.append($('</pre>
<div>',{'class':'autocompleteextended-buttoncontainer'})); if (options.add) { if (settings.buttons.useIcons) father.find('.autocompleteextended-buttoncontainer').append($('<button>', { 'class': 'new' }).button({ text: false, icons: { primary: 'ui-icon-plus'} })); else father.find('.autocompleteextended-buttoncontainer').append($('</button><button>', { 'class': 'new' }).text(settings.buttons.textNew).button()); father.find('.autocompleteextended-buttoncontainer').find('button.new').click(function () { father = $(this).parent().parent(); $('body #autocompleteextended-new-item').dialog('option', { buttons: { 'OK': function () { var input = father.find('input'); if (settings.windows.add.onOK) settings.windows.add.onOK($(this).find('input')); input.val($(this).find('input').val()); if (settings.multiple) { input.attr('readonly','readonly').addClass('readonly').next().find('button').remove(); IntFunz.addActionButton({ remove: true }); father = father.parent(); IntFunz.InitPlugin({ addInput: true }); } $(this).dialog('close'); }, 'Cancel': function () { $(this).dialog('close'); } }, close: function (event, ui) { if (settings.windows.add.onClose) settings.windows.add.onClose(); if (settings.callback) settings.callback(); } }); $('body #autocompleteextended-new-item').dialog('open'); $('body #autocompleteextended-new-item input').val('').focus(); return false; }); }; if (options.list) { if (settings.buttons.useIcons) father.find('.autocompleteextended-buttoncontainer').append($('</button><button>', { 'class': 'list' }).button({ text: false, icons: { primary: 'ui-icon-script'} })); else father.find('.autocompleteextended-buttoncontainer').append($('</button><button>', { 'class': 'list' }).text(settings.buttons.textList).button()); father.find('.autocompleteextended-buttoncontainer').find('button.list').click(function () { father = $(this).parent().parent(); $('body #autocompleteextended-list-elements').dialog('option', { buttons: { 'OK': function () { var input = father.find('input'); input.val($(this).find('input.autocompleteextended-list-elements-ui').val()); if (settings.multiple) { input.attr('readonly', 'readonly').addClass('readonly').next().find('button').remove(); IntFunz.addActionButton({ remove: true }); father = father.parent(); IntFunz.InitPlugin({ addInput: true }); } $(this).dialog('close'); }, 'Cancel': function () { $(this).dialog('close'); } }, close: function (event, ui) { if (settings.windows.list.onClose) settings.windows.list.onClose(); if (settings.callback) settings.callback(); } }); $('body #autocompleteextended-wait-dialog').dialog('open'); $.ajax({ url: settings.SearchUri, dataType: "json", success: function (data) { $('body #autocompleteextended-list-elements select').empty(); $.each(data, function (i, v) { $('body #autocompleteextended-list-elements select').append(' ' + v.value + ' ') }); $('body #autocompleteextended-list-elements select').combobox(); $('body #autocompleteextended-wait-dialog').dialog('close'); $('body #autocompleteextended-list-elements').dialog('open'); $('body #autocompleteextended-list-elements input').focus(); } }); return false; }); }; if (options.remove) { if (settings.buttons.useIcons) father.find('.autocompleteextended-buttoncontainer').append($('</button><button>', { 'class': 'delete' }).button({ text: false, icons: { primary: 'ui-icon-minus'} })); else father.find('.autocompleteextended-buttoncontainer').append($('</button><button>', { 'class': 'delete' }).text(settings.buttons.textDelete).button()); father.find('.autocompleteextended-buttoncontainer').find('button.delete').button().click(function () { var obj = $(this).parent().parent(); $('body #autocompleteextended-confirm-delete').dialog('option', { buttons: { 'OK': function () { obj.remove(); $(this).dialog('close'); }, 'Cancel': function () { $(this).dialog('close'); } }, close: function (event, ui) { if (settings.windows.remove.onClose) settings.windows.remove.onClose(); if (settings.callback) settings.callback(); } }); $('body #autocompleteextended-confirm-delete').dialog('open'); return false; }); } }, setInput:function () { if (settings.withNew) IntFunz.addActionButton({ add: true }); if (settings.withList) IntFunz.addActionButton({ list: true }); // Enable autocomplete var input = father.find('input'); input.autocomplete({ minLength: settings.minLength, source: function (request, response) { if (request.term in cache)response(cache[request.term]); else { $.ajax({ url: settings.SearchUri, dataType: "json", data: request, success: function (data) { cache[request.term] = data; response(data); } }); } }, select: function (event, ui) { if (settings.multiple) { input.val(ui.item).attr('readonly', 'readonly').addClass('readonly').next().find('button').remove(); IntFunz.addActionButton({ remove: true }); father = father.parent(); IntFunz.InitPlugin({ addInput: true }); } }, change: function (event, ui) { if (!ui.item) { // remove invalid value, as it didn't match anything $(this).val(""); return false; } }, open: function (event, ui) { input.autocomplete("widget").width(input.width()); } }); input.keypress(function () { father = $(this).parent() }); if (settings.useRegEx) input.filter(function () { return this.value.match(settings.RegExPattern); }); return input; }, InitPlugin:function (options) { if (settings.multiple) { count++; father.addClass('autocompleteextended-father'); father.append(' '); if (options.addInput) { var theNewInput = theInput.clone(); father = father.find('div.autocompleteextended-grp-' + count); theNewInput.attr('id', theInput.attr('id') + count); father.append(theNewInput); } else { father.find('div.autocompleteextended-grp-' + count).append(father.find('input')); father = father.find('div.autocompleteextended-grp-' + count); } }else father.addClass('autocompleteextended-grpinputs').addClass('ui-widget'); return IntFunz.setInput(); }, setvalue:function(items){ if(settings.multiple){ $.each(items, function (i, v) { input.val(v); input.attr('readonly', 'readonly').addClass('readonly').next().find('button').remove(); IntFunz.addActionButton({ remove: true }); father = father.parent(); input = IntFunz.InitPlugin({ addInput: true }); }); }else input.val(items); } }; if(typeof(options)=='string'){ if(IntFunz[options])IntFunz[options].apply(null,Array.prototype.slice.call(arguments,1)); }else if(options){ settings = $.extend(settings, options || {}); $(this).data('autocompleteextendedsettings',settings); // Run plugin AutocompleteExtended.init(); // Do this only if the input is not already binded if (!input.attr('autocomplete')){ // Get the input as it is, so we can add more like this after theInput = input.clone(); father = input.parent(); IntFunz.InitPlugin({ addInput: false }); if(settings.defaultvalue)IntFunz.setvalue(settings.defaultvalue); } $(this).data('autocompleteextendedcache',cache); $(this).data('autocompleteextendedfather',father); $(this).data('autocompleteextendedoriginalinput',theInput); $(this).data('autocompleteextendedcount',count); } }; // Public methods - can be called from client code // Prepare the code for the windows that would be loaded AutocompleteExtended.init = function(initoptions){ var initsettings = $.extend({ add: { title: 'Add item', text: 'Write here the value' }, list: { title: 'Items list' }, remove: { title: 'Are you sure?', text: 'Do you really want to delete the selected item?' }, wait: { title: 'Now loading...', text: 'The items are now loading. This may take some time...' } }, initoptions || {}); // Prepare the windows if ($('body #autocompleteextended-new-item').length == 0) { $('body').append('
<div id="autocompleteextended-new-item" title="' + initsettings.add.title + '">
<fieldset style="margin: 0; padding: 0; border: none;"><label style="display: block; margin-bottom: 5px;">' + initsettings.add.text + '</label> <input class="text ui-widget-content ui-corner-all" style="width: 330px;" type="text" /></fieldset>
</div>
'); $('body #autocompleteextended-new-item').dialog({ autoOpen: false, height: 180, width: 370, modal: true, resizable: false, draggable: false, open: function (event, ui) { //hide close button. $(this).parent().children().children('.ui-dialog-titlebar-close').hide(); } }); } if ($('body #autocompleteextended-list-elements').length == 0) { $('body').append('
<div id="autocompleteextended-list-elements" title="' + initsettings.list.title + '"></div>
'); $('body #autocompleteextended-list-elements').dialog({ autoOpen: false, width: 345, modal: true, resizable: false, draggable: false, open: function (event, ui) { //hide close button. $(this).parent().children().children('.ui-dialog-titlebar-close').hide(); } }); } if ($('body #autocompleteextended-wait-dialog').length == 0) { $('body').append('
<div id="autocompleteextended-wait-dialog" title="' + initsettings.wait.title + '">' + initsettings.wait.text + '</div>
'); $('body #autocompleteextended-wait-dialog').dialog({ autoOpen: false, height: 100, modal: true, resizable: false, closeOnEscape: false, draggable: false, open: function (event, ui) { //hide close button. $(this).parent().children().children('.ui-dialog-titlebar-close').hide(); } }); } if ($('body #autocompleteextended-confirm-delete').length == 0) { $('body').append('
<div id="autocompleteextended-confirm-delete" title="' + initsettings.remove.title + '">' + initsettings.remove.text + '</div>
'); $('body #autocompleteextended-confirm-delete').dialog({ autoOpen: false, height: 160, modal: true, resizable: false, draggable: false, open: function (event, ui) { //hide close button. $(this).parent().children().children('.ui-dialog-titlebar-close').hide(); } }); } }; // This will initialize a method that can make your select skinned with jQuery ui and autocompleted (so you can type in and filter the items) $.widget("ui.combobox", { _create: function () { var self = this; var select = this.element.hide(); var input = $(" </button><input type="text" /><button>").insertAfter(select).autocomplete({ source: function (request, response) { var matcher = new RegExp(request.term, "i"); response(select.children("option").map(function () { var text = $(this).text(); if (this.value &amp;&amp; (!request.term || matcher.test(text))) return { id: this.value, label: text.replace(new RegExp("(?![^&amp;;]+;)(?!&lt;[^&lt;&gt;]*)(" + $.ui.autocomplete.escapeRegex(request.term) + ")(?![^&lt;&gt;]*&gt;)(?![^&amp;;]+;)", "gi"), "$1"), value: text }; })); }, open: function (event, ui) { input.autocomplete("widget").width(input.width() - 4).height(180).css('overflow-y', 'scroll').css('overflow-x', 'hidden'); }, delay: 0, change: function (event, ui) { if (!ui.item) { // remove invalid value, as it didn't match anything $(this).val(""); return false; } select.val(ui.item.id); self._trigger("selected", event, { item: select.find("[value='" + ui.item.id + "']") }); }, minLength: 0 }).addClass("ui-widget ui-widget-content ui-corner-left ui-combobox autocompleteextended-list-elements-ui").css('width', '280px').css('padding', '0.4em 0'); $("</button><button>",{"tabIndex":-1,"title":"Show All Items","style":"padding:0;margin:5px 0;vertical-align:bottom;display:inline-block"}).text(" ").insertAfter(input).button({ icons: { primary: "ui-icon-triangle-1-s" }, text: false }).removeClass("ui-corner-all").addClass("ui-corner-right ui-button-icon").click(function () { // close if already visible if (input.autocomplete("widget").is(":visible")) { input.autocomplete("close"); return; } // pass empty string as value to search for, displaying all results input.autocomplete("search", ""); input.focus(); return false; }); } }); $.fn.extend({ autocompleteextended:function(){ var args = arguments; this.each(function(){AutocompleteExtended.apply(this,args)}); } }); })(jQuery);

How to fix baloon height with images in Google Maps API v2

Hi,

today i’m going to talk about how to fix the (annoying?) baloon height when it has images in itself, on Google Maps API v2. Of course, i’ve had so much headache with this problem, i’ve searched all around the web to find how to fix it and of course i found two ways that tell’s how to fix it.

The first method implies that you have to set a fixed height for the baloon, but personaly, i was searching for a more flexible way to get it to work (let’s say that i need to have multiple heights on the baloon because my image would be sometimes in 16:9 and sometimes in 4:3). The second method itself tells about an async load of the image in the baloon about Google Maps API itself, so if you see that the text is going outside the baloon in fact it’s not a text fault but Google cannot found the size of the image because it wasn’t loaded at the moment it was calculating.

So, after reading this i thought “why not mixing them both?” and fact i did it. What i did to fix the height in the baloon, was two simple steps: cache the image before the baloon opens and (ta-da) set an height and a width in the image tag (the height and the width of the image of course). And bingo! the baloon opens quite well and without any sort of problems in any browser (that goes from IE6-8, Firefox 2.x-3.x, Safari, Chrome and Opera).

So, see my snippet, as an example, to understand much more the code and how it does work. If you’re asking yourself why did i use jQuery to do things is because of the more fast coding that it let you to do and because it’s much better to load javascript only after the page has fully loaded (so it’s much more compatible and fluid). Again, the document.ready is not always fired that way if you use that event (eg Internet Explorer do that in other way) so i prefer to use much more safe way that covers all the browsers that we know.

I hope you will find this useful, because i didn’t found any article that talk about this in the internet so i hope that people will have less headache than me next time we will have to do so :)

We’ll catch the next time with maybe some other cool things i’m preparing like a more functional jQueryUI Autocomplete (with multiple input texts and so on) that i’ll explain when the time will come.

Thanks again for the visit.

var imageCache;
(function($){
	var cGIcon =  new GIcon(G_DEFAULT_ICON) ;
	marker = new GMarker(new GLatLng(dLat,dLng),{icon:cGIcon});
	GEvent.addListener(marker, "click", function(){
		// Cache the image before loading the baloon - This way is preferred because of the Garbage Collector of Firefox. If you put this on a var, firefox will think that is not usefull so will throw it away, without loading the image
		if(imageCache) imageCache = $('body').append('<div id="img-cache" style="display:none/>').children('#img-cache'); //So we will create a DIV to put image cache inside it at the end of the body
		$('<img/>').attr('src', 'http://path/to/the/image.jpg').appendTo(imageCache); // Finally, append the image itself
		marker.openInfoWindowHtml('<div class="content"><h1>Title</h1><img src="http://path/to/the/image.jpg" height="100" width="100"><p>This is the text of the baloon</p></div>');
	}
	map.addOverlay(marker);
})(jQuery);

Let’s make Google Chrome Live! – Part 2

Hello again,

sorry for this late post that i’ve promised a month ago. But who cares, the important thing is that we’re here alive and kicking again or not? :)

Today i would like to tell you how to create a GUI loader for your Google Chrome Live CD, because if you remember the last post, we had made it but only in (maybe horrible?) Console version. Before posting the code as usual i would like to tell you what you have to do before.

To make a GUI loader for a CD is just a pain in the ass (believe me) because you have to think two things: first, is for what platform you’re going to develop to, and second you have to choose a method that is compatible with all the forms, versions and types of that platform. In this case i covered all the Windows platforms (from Windows 95 to Windows 7) but as you already know between them, there are so much differences in the core of the platform that made me to choose the only one that i know that it just works: i’m talking about Windows SDK.

Yeah, maybe you’re thinking “wtf?” or “just crazy…why didn’t you choose .NET platform?”, but i can answer both of those questions with: didn’t you know that Windows 95 to Windows ME do not have .NET support? And because Windows is made in Pure C, why can’t we use such a great language like that one? Windows SDK was made with only one scope in mind: cover all the windows platforms, to let developers create application that just works (but they’re just “much difficult” to code).

OK so, now that we know what are we going to do, before copying and compiling the source that will be posted after this, you must do two things: create an empty C++ project with Microsoft Visual C++ 2005/2008/2010 (also Express Edition works) and create a BMP image to put when our loader is working. After this remember to include that one in a file called Resource.rc. That is, copy and paste the code into your Main.cpp file and compile :)

Easy huh? If you’re curious to know much about the code, read the comments (if you need more, just let me know) and continue to follow me as i’m going to post some nice other things, like how to fix baloon height size in Google Maps API v2.

Thanks for reading, see you in the next post.

EDIT: I’ve attached a simple project with all of this combined, so just download, unzip and compile. To do so, you need Microsoft Visual C++ 2010 Express Edition (otherwise you can copy and paste the code). Download it now!

Main.cpp

#include "resource.h"
#include <direct.h> // for getcwd
#include <stdlib.h>// for MAX_PATH
#include <windows.h>
#include <windowsx.h>

#define IDT_TIMER1 1000

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
HBITMAP hBitmap;
PAINTSTRUCT ps;
HDC hdc;

// Get information about Chrome
PROCESS_INFORMATION pif;  //Gives info on the thread and..
						  //..process for the new process
STARTUPINFO si;           //Defines how to start the program

/* Function CenterWindow(), Centers Window */
VOID CenterWindow(HWND hwnd, HWND hwndParent, int Width, int Height)
{

	/* Variables */
	RECT rc;

	/* If Parent Window Is Set As Null, Get The Desktop Window */
	if(hwndParent == NULL)
		hwndParent = GetDesktopWindow();

	/* Get Parent Client Area Measurements */
	GetClientRect(hwndParent, &rc);

	/* Center The Window */
	MoveWindow(
		hwnd,
		(rc.right - rc.left - Width) / 2,
		(rc.bottom - rc.top - Height) / 2,
		Width,
		Height,
		TRUE
	);

	return;

}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
  static char szAppName[] = "My CD Loader" ;
  HWND        hwnd ;
  MSG         msg ;
  WNDCLASSEX  wndclass ;

  wndclass.cbSize        = sizeof (wndclass) ;
  wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
  wndclass.lpfnWndProc   = WndProc ;
  wndclass.cbClsExtra    = 0 ;
  wndclass.cbWndExtra    = 0 ;
  wndclass.hInstance     = hInstance ;
  wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
  wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
  wndclass.lpszMenuName  = NULL ;
  wndclass.lpszClassName = szAppName ;
  wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;

  RegisterClassEx (&wndclass) ;

  hwnd = CreateWindow(szAppName,   // window class name
    NULL,     // window caption
    WS_POPUP,     // window style
    CW_USEDEFAULT,           // initial x position
    CW_USEDEFAULT,           // initial y position
    367,           // initial x size
    192,           // initial y size
    NULL,                    // parent window handle
    NULL,                    // window menu handle
    hInstance,               // program instance handle
    NULL) ;		       // creation parameters

  ShowWindow (hwnd, iCmdShow) ;
  UpdateWindow (hwnd) ;
  CenterWindow(hwnd, NULL, 367, 192);
  SetTimer(hwnd,             // handle to main window
    IDT_TIMER1,            // timer identifier
    1000,                 // 10-second interval
    (TIMERPROC) NULL);     // no timer callback 

  while (GetMessage (&msg, NULL, 0, 0))
  {
    TranslateMessage (&msg) ;
    DispatchMessage (&msg) ;
  }

  return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
  switch (iMsg)
  {
	case WM_CREATE :
	  {
		  // The bitmap should be stored as a resource in the exe file.
		  // We pass the hInstance of the application, and the ID of the
		  // bitmap to the LoadBitmap API function and it returns us an
		  // HBITMAP to a DDB created from the resource data.
		  HINSTANCE hInstance = GetWindowInstance(hwnd);
		  hBitmap = LoadBitmap(hInstance,MAKEINTRESOURCE(IDB_BITMAP1));

		  // Start Google Chrome
		  // _MAX_PATH is the maximum length allowed for a path
		  char CurrentPath[_MAX_PATH];
		  char SysLaunch[_MAX_PATH];
		  // use the function to get the path
		  _getcwd(CurrentPath, _MAX_PATH);
		  strcpy_s(SysLaunch, CurrentPath);
		  strcat_s(SysLaunch, "\\GoogleChromePortable\\GoogleChromePortable.exe --start-maximized --app=\"");
		  strcat_s(SysLaunch, CurrentPath);
		  strcat_s(SysLaunch, "\\start.html\"");

		  ZeroMemory(&si,sizeof(si)); //Zero the STARTUPINFO struct
		  si.cb = sizeof(si);         //Must set size of structure

		  BOOL bRet = CreateProcess(
				NULL, //Path to executable file
				SysLaunch,   //Command string - not needed here
				NULL,   //Process handle not inherited
				NULL,   //Thread handle not inherited
				FALSE,  //No inheritance of handles
				0,      //No special flags
				NULL,   //Same environment block as this prog
				NULL,   //Current directory - no separate path
				&si,    //Pointer to STARTUPINFO
				&pif);   //Pointer to PROCESS_INFORMATION

		  if(bRet == FALSE)
		  {
			MessageBox(HWND_DESKTOP,"Cannot run Google Chrome.","",MB_OK);
			PostQuitMessage (0);
		  }

		  return 0;
	  }
	case WM_PAINT :
		{
		  hdc = BeginPaint (hwnd, &ps);
		  // To paint with a DDB it first needs to be associated
		  // with a memory device context. We make a DC that
		  // is compatible with the screen by passing NULL to
		  // CreateCompatibleDC.
		  // Then we need to associate our saved bitmap with the
		  // device context.

		  HDC hdcMem = CreateCompatibleDC(NULL);
		  HBITMAP hbmT = SelectBitmap(hdcMem,hBitmap);

		  // Now, the BitBlt function is used to transfer the contents of the
		  // drawing surface from one DC to another. Before we can paint the
		  // bitmap however we need to know how big it is. We call the GDI
		  // function GetObject to get the relevent details.
		  BITMAP bm;
		  GetObject(hBitmap,sizeof(bm),&bm);

		  BitBlt(hdc,0,0,bm.bmWidth,bm.bmHeight,hdcMem,0,0,SRCCOPY);

		  // Now, clean up. A memory DC always has a drawing
		  // surface in it. It is created with a 1X1 monochrome
		  // bitmap that we saved earlier, and need to put back
		  // before we destroy it.
		  SelectBitmap(hdcMem,hbmT);
		  DeleteDC(hdcMem);

		  // EndPaint balances off the BeginPaint call.
		  EndPaint (hwnd, &ps);
		  return 0;
		}
	case WM_TIMER :
		{
			switch (wParam)
			{
				case IDT_TIMER1 :
					{
						if(!WaitForInputIdle(pif.hProcess, 0))
						{
							CloseHandle(pif.hProcess);   //Close handle to process
							CloseHandle(pif.hThread);    //Close handle to thread
							PostQuitMessage (0) ; // Quit from App
						}
						break;
					}
			}
			break;
		}
	case WM_DESTROY :
		  PostQuitMessage (0) ;
		  return 0 ;
  }

  return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}

Resource.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Italian (Italy) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ITA)
LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

IDB_BITMAP1             BITMAP                  "Path\\To\\My\\File\\loading.bmp"
#endif    // Italian (Italy) resources
/////////////////////////////////////////////////////////////////////////////

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//

/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED