Monday, 20 August 2012

Cheating littlealchemy

If you got stuck and can't find any more combinations and you want to create more, here is a little script that will activate all the elements:


alchemy.history.parents = [];

for(var i=0;i<base.base.length;i++){
if(base.base[i][0] != false){
for(var j=0;j<base.base[i].length;j++){
alchemy.history.parents.push(base.base[i][j])
}
}
}

localStorage.setItem("history", JSON.stringify(alchemy.history));


Just paste this code in the browser's console (F12 for firefox's firebug or google chrome's console) and after just refresh.

If you want a complete list of all the combinations possible run this code:

WARNIGN: this script may take a long time to compile and you may get "stop script" alerts. If you are patient and click continue you will see the full list. The time to finish depends on the pc and the browser (fastest is goole chrome)

var html = [];

var l = base.length;
var s = 0;

for(var ii=s;ii<s+l;ii++){
for(var jj=ii;jj<s+l;jj++){
var r = alchemy.sex([ii,jj]);
if(r.length>0){
html.push('<div><img src="img/base/'+(ii+1)+'.png" class="boxImg"> + <img src="img/base/'+(jj+1)+'.png" class="boxImg"> = ');
for(var k=0;k<r.length;k++){
html.push('<img src="img/base/'+(r[k]+1)+'.png" class="boxImg">')
if(k+1<r.length) html.push(' , ')
}
html.push('</div>')
}
}
}

$('<div>').html('<div style="height:350px;overflow:auto">'+html.join('')+'</div>').dialog({
height: 400,
width: 800,buttons: {
'OK': function () {
$(this).dialog("close");
}
},modal: true,
draggable: true,
});
/**/


If there are at least 5 persons that ask me to make a web page where you can see the complete tutorial I will make it so you won't have to use the script every time.

Friday, 15 June 2012

Update Samsung GT-I5500 from 2.2 to 2.3.7 - CyanogenMod 7.2 1.9 MADTEAM

EDIT: HOW ABOUT UPGRADING TO 4.0 (CyanogenMod 9) ?

I thought it would be useful to share this info, if you have a phone Galaxy 5/Galaxy 550/GT-I5500/Europa Galaxy or whatever you call it. The method was tested on several terminals and works without any problems. We observed significant performance boost, in some cases beating a Galaxy S, and that without overclocking the processor.
Note that if the phone will break for various reasons and the firmware is still installed, guarantee is lost as a result of these changes. But in most cases you can return to official firmware that came with the phone. The firmware comes with Root, superuser and BusyBox installed, and is based on Android 2.3.7, the latest version of Android Gingerbread. Different versions of the firmware have been run stable on my phone for about 2 months. Note that it is more stable than stock firmware!

To streamline the process of upgrading the firmware, instead of uploading individual files I chose to put them all in one archive on RapidShare, which offers a much higher transfer rate.

Follow this link to download files: 
Cyanogen.rar

Once you have downloaded the file, use WinRAR ( winRAR ) to unpack it.

We should have a folder that includes the following files:




After we made sure we have all the files needed to continue, before the writing of the firmware  we need to install the USB drivers for Samsung mobile phone, that if we don't have installed Kies.
Drivers as standalone package can be found here: drivers .

If the installation went smoothly, we finally get to install the firmware on the phone.
We need original Samsung USB cable (preferably, but should work without problems using a new cable).

My request is to follow these steps in the order they appear here, or you can meet with some problems.

1. Backup data you consider important, no one will take responsibility for lost data! Speaking of contacts, simply export the SD card, then copy them into the computer later. For others: photos, videos, etc. I suspect that you have already guessed, like, copy them into the computer. Although small chances, the processes for firmware upgrade can result in an unexpected way.

2. Turn off the phone after you enter the download mode. To do this it is necessary to hold simultaneously the volume down key and the center button(center of the joystick) and the power button).

3. Connect the phone to the PC via the USB cable. If your phone has not been connected until now in download mode, down in the Windows notification area there will be displayed a balloon telling you that new hardware is being installed. Wait until the process is completed, then open "Odin Multi Downloader v4.28" included in the archive provided by me.

4. First we rewrite the baseband phone with one that is "certified" to be fully compatible with Cyanogen Mod. Version is included and recommended by me "I5500XWJP4". From Odin, we click on the OPS file and select "EUROPA_I5508_v1.0.ops". Select the option one package, then we click on the tab with the same name under "Select Integrated Package - Check One Option Package" and select file "MODEM_I5500XWJP4.tar.md5", followed by a click on "Start". Wait until the process is completed, the phone should restart and boot into the OS.

5. Turn off the phone again, enter download mode again, only this time instead of "MODEM_I5500XWJP4.tar.md5", we select "ClockworkMod-Galaxy5-v0.7", followed as before by the "Start ".

6. After completing the phone will automatically install the Recovery ClockWorkMod. Navigating through the menus is done with keys up / down and selecting with the middle button. The first thing you need to do is to select "Wipe Data / Factory Reset", navigate to the Yes (like you have to do for each action of CWM).

7. Now we move on to installing packages. Go to "Mount and storage", then "Mount USB Storage". Copy files "update-cm-7.2.0-RC0-galaxy5-v1.9-MADTEAM-signed", "Galaxy5-v1.9-hotfix3", "gapps-gb-20110828-signed", "TouchWiz_3.0_Port_v0.7 "removable disk (SD card of the phone) again appeared in My Computer. We expect to complete the process successfully and give "Unmount USB Storage" in the CWM phone. Back to main menu using the key back. Navigate to the "Install zip from SD card" -> "Chose zip from SD card". Select "update-cm-7.2.0-RC0-galaxy5-v1.9-MADTEAM-signed", followed by Yes.

8. Now we install the hotfix, just like the previous step, only this time select, after the "Chose zip from SD card" select file "Galaxy5-v1.9-hotfix3", followed by Yes.

9. The last step required, that if you want to use the Android Market, and you want this more than sure, this is installing applications including Google. Mentioned is that applications like YouTube, Maps, etc.. can easily install from the Android Market. As far as the "Chose zip from SD card", this time select "gapps-gb-20110828-signed", followed by Yes.

10. Installation TouchWiz 3.0 (or the GUI Launcher, which is included in the Samsung's firmware). Not different, as "Chose zip from SD card", choose "TouchWiz_3.0_Port_v0.7", and finally Yes.

11. Give "Reboot System" from the main menu and wait for phone to boot. The first time it takes longer, since we need to index new files, so there should be no concern. Follow the instructions on the screen, everything should go smoothly. If you have installed and TouchWiz, at some point you will be prompted to start the Launcher. You can check that the option selected by default start with the phone. If at any time you want to get rid of one of them you can do this using Titanium Backup that you find on the Market.

This version does not require battery recalibration, because with the introduction of version 1.9 of this port driver returned to the battery of your original firmware. This version of firmware with all the hotfixes included was extensively tested by me all day and I can say that with the new version was going to respond much better if the phone orders and animations are included purely ICS and simply adorable! I hope I managed somehow to help users without technical knowledge too advanced or do not want to fight too much. I'll answer any questions as long as they are in connection with this firmware and / or telephone. All the best!


If this post helped you in any way don't be shay and treat me to a bear, what do you say?



Monday, 16 January 2012

Sugarcrm custom chart

Creating a sugar chart dashlet is done the same way as you would create a simple dashlet, just extending 2 functions, for this reason I will not cover the way a sugar dashlet is created. Notes: The dashlets will be named  "CustomLineDashlet" and "CustomPieDashlet" and the module is "abc_Sample". You can use the predefined sugar chart definitions for parsing data (modules\Charts\chartdefs.php) or you can create your own to meet any custom demands. This is done by editing(creating if dose not exist) "custom\Charts\chartDefs.ext.php". For CustomPieDashlet : custom\Charts\chartDefs.ext.php:
$chartDefs['custom_pie_chart'] = array('type' => 'code',
 'id' => 'custom_pie_chart',
 'label' => 'custom_pie_chart label',
 'chartUnits' => 'The unit definition',
 'chartType' => 'pie chart',
 // important value that will be used to group the data
 'groupBy' => array( 'name' ), 
 'base_url'=> 
  array(  'module' => 'abc_Sample',
   'action' => 'index',
   'query' => 'true',
   'searchFormTab' => 'advanced_search',
   )   
);
custom\modules\abc_Sample\Dashlets\CustomPieDashlet\CustomPieDashlet.php
....
public function display() {
 $currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
 require ("modules/Charts/chartdefs.php");
 // here we load the chart definition (custom\Charts\chartDefs.ext.php)
 $chartDef = $chartDefs['custom_pie_chart'];
 require_once ('include/SugarCharts/SugarChart.php');
 $sugarChart = new SugarChart();
 $sugarChart -> setProperties('', translate('LBL_OPP_SIZE', 'Charts') . ' ' . currency_symbol . '1' . translate('LBL_OPP_THOUSANDS', 'Charts'), $chartDef['chartType']);
 $sugarChart -> base_url = $chartDef['base_url'];
 $sugarChart -> group_by = $chartDef['groupBy'];
 $sugarChart -> url_params = array();
 $sugarChart -> getData($this -> constructQuery());
 $xmlFile = $sugarChart -> getXMLFileName($this -> id);
 $sugarChart -> saveXMLFile($xmlFile, $sugarChart -> generateXML());
 return $this -> getTitle('
') . '
' . $sugarChart -> display($this -> id, $xmlFile, '100%', '480', false) . '
'; } protected function constructQuery() { // this is the query used to get data and create the xml // for pie chart we have a simple query $query = " SELECT count(*) as total, name FROM table GROUP BY name ORDER BY name"; return $query; }
For CustomLineDashlet : custom\Charts\chartDefs.ext.php:
$chartDefs['custom_line_chart'] = array(  'type' => 'code',
 'id' => 'custom_line_chart',
 'label' => 'custom_line_chart label',
 'chartUnits' => 'The unit definition',
 'chartType' => 'line chart',
 // the legend will be the name and the x intervals will be the date
 'groupBy' => array( 'date','name' ), 
 'base_url'=> 
  array(  'module' => 'sam_Sample',
   'action' => 'index',
   'query' => 'true',
   'searchFormTab' => 'advanced_search'
 )   
);
custom\modules\abc_Sample\Dashlets\CustomLineDashlet\CustomLineDashlet.php
....
public function display() {
 $currency_symbol = $GLOBALS['sugar_config']['default_currency_symbol'];
 require ("modules/Charts/chartdefs.php");
 // here we load the chart definition (custom\Charts\chartDefs.ext.php)
 $chartDef = $chartDefs['custom_line_chart'];
 require_once ('include/SugarCharts/SugarChart.php');
 $sugarChart = new SugarChart();
 $sugarChart -> setProperties('', translate('LBL_OPP_SIZE', 'Charts') . ' ' . currency_symbol . '1' . translate('LBL_OPP_THOUSANDS', 'Charts'), $chartDef['chartType']);
 $sugarChart -> base_url = $chartDef['base_url'];
 $sugarChart -> group_by = $chartDef['groupBy'];
 $sugarChart -> url_params = array();
 $sugarChart -> getData($this -> constructQuery());
 $xmlFile = $sugarChart -> getXMLFileName($this -> id);
 $sugarChart -> saveXMLFile($xmlFile, $sugarChart -> generateXML());
 return $this -> getTitle('
') . '
' . $sugarChart -> display($this -> id, $xmlFile, '100%', '480', false) . '
'; } protected function constructQuery() { // this is the query used to get data and create the xml // for pie chart we have a simple query $query = " SELECT count(*) as total, name, date FROM table GROUP BY date,name ORDER BY name"; return $query; }
the meta files ( custom\modules\abc_Sample\Dashlets\CustomLineDashlet\CustomPieDashlet.meta.php and custom\modules\abc_Sample\Dashlets\CustomLineDashlet\CustomLineDashlet.meta.php) will look like this:
$dashletMeta['CustomSamplePieCart'] = array('module'  => 'abc_Sample',
            'title'       => 'Custom Pie Chart', 
                                          'description' => 'Custom description',
                                          'icon'        => 'themes/default/images/icon_sam_Sample_32.gif',
                                          'category'    => 'Charts');
and
$dashletMeta['CustomSampleLineCart'] = array('module'  => 'abc_Sample',
            'title'       => 'Custom Chart', 
                                          'description' => 'Custom description',
                                          'icon'        => 'themes/default/images/icon_sam_Sample_32.gif',
                                          'category'    => 'Charts');


Notes:
custom\Charts\chartDefs.ext.php is the place where you will define your chart type, important fields:
'groupBy' => array( 'date','name' ) (the x and legend fields)
'chartType' => 'line chart', (the swf name, php will capitalize and add .swf, so it will be lineChar.swf)

in the sql your x values will have to coincide (be the same) for all lines

Sunday, 1 January 2012

Gotomeeting php api(oauth) create meting

This is from https://developer.citrixonline.com/forum/create-meeting-error , the code from bcantoni:
Create a METING:


 <;∧php

// sample GoToMeeting API call: create meeting
// docs: https://developer.citrixonline.com/api/GoToMeeting%2520REST%2520API/Create%2520Meeting-150

// assume have valid $access_token from OAuth flow
$access_token = 'xyz';

$url = "https://api.citrixonline.com/G2M/rest/meetings";
$headers = array (
    "Accept: application/json",
    "Content-Type: application/json",
    "Authorization: OAuth oauth_token=$access_token"
);
$data = array ('test meeting',
    'starttime' => '2012-02-01T08:00:00',
    'endtime' => '2012-02-01T09:00:00',
    'timezonekey' => '67', // Pacific time
    'meetingtype' => 'Scheduled',
    'passwordrequired' => 'false',
    'conferencecallinfo' => 'Hybrid' // normal PSTN + VOIP options
);
$data_json = json_encode ($data);
 
$ch = curl_init();
curl_setopt_array ($ch, array (
    CURLOPT_URL => $url,
    CURLOPT_HEADER => false,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $data_json,
));
 
$results = curl_exec ($ch);
$info = curl_getinfo ($ch);
curl_close ($ch);
 
print "data sent: $data_json\n";
//print "headers sent: " . print_r($headers,1) . "\n";
//print "curl info: " . print_r($info,1) . "\n";
print "data returned: " . print_r($results,1 ) . "\n";
//print "data returned decoded: " . print_r(json_decode($results),1) . "\n";

And the response:

$ php post.php
 
data sent: {"subject":"test meeting","starttime":"2012-02-01T08:00:00","endtime":"2012-02-01T09:00:00","timezonekey":"67","meetingtype":"Scheduled","passwordrequired":"false","conferencecallinfo":"Hybrid"}
 
data returned: {"joinURL":"https:\/\/www3.gotomeeting.com\/join\/123456789","maxParticipants":26,"uniqueMeetingId":2000007959645,"meetingid":123456789}
EDIT:


thaks for the response, but I still get the same error with your code :). Here is the output from the php:

data sent:
Array
(
    [0] => test meeting
    [starttime] => 2012-02-01T08:00:00
    [endtime] => 2012-02-01T09:00:00
    [timezonekey] => 67
    [meetingtype] => Scheduled
    [passwordrequired] => false
    [conferencecallinfo] => Hybrid
)

data returned:
{"int_err_code":"Server.userException","msg":"java.lang.NumberFormatException: Invalid date/time"}curl info:
Array
(
    [url] => https://api.citrixonline.com/G2M/rest/meetings
    [content_type] => text/xml; charset=utf-8
    [http_code] => 404
    [header_size] => 224
    [request_size] => 483
    [filetime] => -1
    [ssl_verify_result] => 20
    [redirect_count] => 0
    [total_time] => 2.797
    [namelookup_time] => 0
    [connect_time] => 0
    [pretransfer_time] => 0.438
    [size_upload] => 712
    [size_download] => 98
    [speed_download] => 35
    [speed_upload] => 254
    [download_content_length] => 98
    [upload_content_length] => 712
    [starttransfer_time] => 2.438
    [redirect_time] => 0
    [certinfo] => Array
        (
        )

)


And from fidler:

POST https://api.citrixonline.com/G2M/rest/meetings HTTP/1.1
Host: api.citrixonline.com
Accept: application/json
Authorization: OAuth oauth_token=1c8187be36bb86997db59e5772268d5a
Content-Length: 712
Expect: 100-continue
Content-Type: application/json; boundary=----------------------------9530b5163dde

------------------------------9530b5163dde
Content-Disposition: form-data; name="starttime"

2012-02-01T08:00:00
------------------------------9530b5163dde
Content-Disposition: form-data; name="endtime"

2012-02-01T09:00:00
------------------------------9530b5163dde
Content-Disposition: form-data; name="timezonekey"

67
------------------------------9530b5163dde
Content-Disposition: form-data; name="meetingtype"

Scheduled
------------------------------9530b5163dde
Content-Disposition: form-data; name="passwordrequired"

false
------------------------------9530b5163dde
Content-Disposition: form-data; name="conferencecallinfo"

Hybrid
------------------------------9530b5163dde--



and the response:


HTTP/1.1 404 Not Found
Date: Sun, 01 Jan 2012 10:57:14 GMT
Server: Apache
Content-Length: 98
Content-Type: text/xml; charset=utf-8
Cneonction: close
Vary: Accept-Encoding
Connection: close

{"int_err_code":"Server.userException","msg":"java.lang.NumberFormatException: Invalid date/time"}

Thursday, 29 December 2011

Gotomeeting php api(oauth) implementation

I had a lot of problems connecting to citrix and making the api work, here are some steps to take:

Oauth integration:

$key = '#';
$secret = '#';

$domain = $_SERVER['HTTP_HOST'];
$base = "/oauth/index.php";
$base_url = urlencode("http://$domain$base");

$OAuth_url = "https://api.citrixonline.com/oauth/authorize?client_id=$key&redirect_uri=$base_url";
$OAuth_exchange_keys_url = "http://api.citrixonline.com/oauth/access_token?grant_type=authorization_code&code={responseKey}&client_id=$key";


if($_SESSION['access_token']) CreateForm();else
if($_GET['send']) OAuth_Authentication($OAuth_url);
elseif($_GET['code']) OAuth_Exchanging_Response_Key($_GET['code'],$OAuth_exchange_keys_url);

function createForm(){
// you are loged in, play around
}


function OAuth_Exchanging_Response_Key($code,$url){
 if($_SESSION['access_token']){
  CreateForm();
  return true;
 }
 $data = getURL(str_replace('{responseKey}',$code,$url));
 
 if(IsJsonString($data)){
  $data = json_decode($data);
  $_SESSION['access_token'] = $data->access_token;
  CreateForm();
 }else{
  echo 'error';
 }
}

function OAuth_Authentication ($url){
 $_SESSION['access_token'] = false;
 header("Location: $url");
}



/*
 * CURL function to get url
 */
function getURL($url,$auth_token = false,$data=false){

 // Initialize session and set URL.
 $ch = curl_init();
 
 curl_setopt($ch, CURLOPT_URL, $url);
        // curl not working local machine, using fiddler
 curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1:8888');
 
 // Set so curl_exec returns the result instead of outputting it.
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
 
 if($auth_token){
  curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: OAuth oauth_token='.$auth_token));
 }
 
 // Get the response and close the channel.
 $response = curl_exec($ch);
 
 /*
  * if redirect, redirect
  */
 $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 
                     if ($code == 301 || $code == 302) { 
                         preg_match('//(.*?)">/', $response, $matches); 
                         $newurl = str_replace('&','&',trim(array_pop($matches))); 
       $response = getURL($newurl);
                     } else { 
                         $code = 0; 
                     }
 curl_close($ch);
 return $response;
}

To start login you need to access ?send=1. I am using fidler as a proxy for https requests because my server is not set up for this.

Sunday, 25 December 2011

JS OOP

If you thought that JS is written only using functions and objects, well, you are kind of wright :). But actually it is the most permissive (in my opinion) OOP language.
A class in js is defined using "function" and when we use something like:
function sum(a,b){
    return a+b;
}
we actually create a new class named "sum". All that we see inside the "{...}" is actually the constructor, so when we call
var c = sum(1,2)
c will be equal to 3, because that is what the constructor of the class returns, the sum of the variables.
If we would use
var c = new sum(1,2);
c would be an instance of the class sum. Of course for the purpose of simplicity class sum dose not have any variables or methods attached to it, so let's go ahead and make this a little more complex.
Let's define a new class that we can also use as a standalone function.

function Animal(name,species){
    // standard (for me) way to use default values for params
    // also the params will be private vars in our class
    name = name || "Unnamed";
    species = species || "Unknown";

    // setup some public vars
    this.animalTitle = name + ' the ' + species;

    // setup some private vars
    var age = 0;

    // create a private methos
    var grow = function(years){
        return age + years;
    }

    // some public methods(functions :) )
    this.getOlder = function(how_much){
        // here we access the private var
        // and a public one
        how_much = how_much || 0;
        age = grow(how_much);
        this.animalTitle = name + ' the ' + species + ' at ' + age + ' years old';
    }

    // using return to demonstrate how it works as a simple function
    return this.animalTitle;
}
Ok, now let's explain more.
name = name || 'Unnamed' is the same as if(name == null) name = 'Unnamed';. BTW, if you expect name, or whatever the param name will be, to be a bool or number you should use ==null or name = (name == null ? "Unnamed" || name) because 0 or false will make act in the expression used by me the same way as null.
this.animalTitle is a public variable that will be readable/writable, will see later.
age is a private variable that can only be used inside the class (also as I said in the inline comments, the params are also considered private vars).
grow is a private method, a function that can be called only inside the class definition.
And getOlder is a public method that can be called or overwritten.
Here is a example on how this can be used as a function:

var my_pet = Animal("Rex","Dog");
alert(my_pet);
// will alert "Rex the Dog"
or as a class definition to create an object:
var my_pet = new Animal("Simba","Lion");
alert(my_pet);
// will alert [object Object]
// to get a better understanding of what this is we can use GC console ot FF firebug and do:
console.log(my_pet)
alert(my_pet.animalTitle)
// will alert 'Simba the Lion'
my_pet.getOlder(10); // calling a public method
alert(my_pet.animalTitle)
// will alert 'Simba the Lion 10 years old'
my_pet.animalTitle = 'Custom title for my pet'; // overwriting a public member of the object
alert(my_pet.animalTitle)
// will alert 'Custom title for my pet'
Next we should talk about the prototype object.
Most of the js community talks about the prototype object as giving the functionability to add public methods/vars to classes. I actually don't like how this sounds and I will refraze it like this: "The prototype object is used to extend methods and variables of a class with the downside that you won't be able to use the private vars". Ok, now let's get back to some code:

// using our class Animal
Animal.prototype.whatToSay = 'I can say this:';
var my_pet = new Animal();
alert(my_pet.whatToSay);
// will alert 'I can say this:'

// we want to add a method named speak
Animal.prototype.speak = function(){
    // we can use public methods from our constructor and also ones we defined with prototype
    alert(this.whatToSay + ' My name is ' + this.animalTitle);
}
// we can't use age or grow() here because we don't have access to anything
// not defined in the constructor (function) with this. (public stuff)
var my_pet2 = new Animal();
my_pet2.speak();
// will alert 'I can say this: I am Unknown the Unnamed'
// why Unnamed? because we didn't gave him a name when we created him and this is the
// default string we gave it
var my_pet3 = new Animal('Bobo','Bear');
my_pet3.speak();
// will alert 'I can say this: I am Bobo the Bear'
Hope this will help somebody on a better understanding of the OOP in JS. Will continue with object members and inheritance.

Friday, 9 December 2011

js async for, very cool

This is something nice. This code can create for interrupts for those long functions in ie, so you can tell the user what is happening :). You can run it in the fireBug's console to see what I mean :).


function async_for_each(object,cbk,limit_callback,end_cbk,limit,timeout,start){
    var l=object.length;
 limit = limit || Math.round(l/100);
 start = start || 0;
 timeout = timeout || 1;
 for(var i=start;ilimit){
   setTimeout(function(){
    async_for_each(object,cbk,limit_callback,end_cbk,limit,timeout,i)
   },timeout);
   limit_callback(i,l);
   return false;
  }else{
   cbk(i,object[i]);
  }
 }
 end_cbk?end_cbk():null;
 return true;
}

var a = [];
a.length = 1000;

async_for_each(a,function(){},function(i,l){console.log("locading %s/%s - %s%",i,l,Math.round(i*100/l))});