programming arts…

X11 Session Forwarding from Linux to Windows without VNC or NXServer

Also in windows you can easy export the X11 sessions of a remote ssh session, for who doesn’t konow how to do it in linux here is it: is very easy, just type ssh -X (or -Y in some ssh versions).

From windows is pretty easy too: you need just an ssh client like putty and an X11 server, like Xming.

For example let’s try to launch baobab, a disk usage analyzer GUI very useful to find files and directory that are wasting space on our linux machine:

in a normal putty ssh session we get:

dev:~# baobab
(baobab:19029): Gtk-WARNING **: cannot open display:

so let’s give it a display to open:

a little demo here:

Capture your screen in seconds

Enjoy!

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , , , , ,

Make awesome and performants live search boxes: Limiting rate of JavaScript function calls

Why not?

Capture your screen in seconds

Sometimes in javascript you feel the need to control function calls for limiting the number of consequential calls, for example, imagine to have a search box that fetches live database data like this:

<label for="livesearch"> Search globally in last published tweets:</label> <input id="livesearch" type="text" onKeyUp="doDBSearch(this.value)"/>

function doDBSearch(txt){
    if (txt.length>3){
           Ajax.searchInLastPublicTweets(txt,callback);
    }
}

Wow! here we have an awesome 2.0 live search box (like the facebook friend search one, or better, because here we have also a live, non-cacheable, backend: a server database with user last messages, messages that needs to be always updated, that are not chaceable in a javascript store to get easly and quickly accessible data for the onKeyUp event, facebook friendships are semi-static data, yes friendships can be considered static data in a one-page context, live logs not! they need to be always updated, they need a server backend.

So what’s the problem in us awesome live searchbox?

When type quickly in the searchbox how many ajax call it will do?
Enough to get the user browser stalled (and the user out of our site).

i need to prevent this (also for server benefit) and to attach the ajax call to an dreamable DOM event like onQuicklyKeyPressSetFinished.

but i’m sorry, this event doesn’t exists like it doesn’t exists yet the possibility to do onUserTought=”codeItForMe(event.needs);”.

so here it is the solution:


var rateLimitTimer=new Array();

function jsExecRateLimitFilterWithArguments(ms,fname,fn){
   var args = Array.prototype.slice.call(arguments);
    rateLimitTimer[fname] && clearTimeout(rateLimitTimer[fname]);
    rateLimitTimer[fname] = setTimeout(function(){
        fn.apply(args.slice(4,args.length)); //call the function with the remaning extra arguments
        rateLimitTimer[fname] = null;
    }, ms);
}

so our onKeyUp becomes:

... onKeyUp="jsExecRateLimitFilter(300,"dbsearchfromtextbox1",doDBSearch,this.value);" ...

here we have the cool: a search operations start only after 300 ms of non-typing!

the second arguments is to needed to have the same function filered by more event listeners e.g

textbox1

... onKeyUp="jsExecRateLimitFilter(300,"dbsearchfromtextbox1",doDBSearch,this.value);" ...

textbox2

... onKeyUp="jsExecRateLimitFilter(200,"anothertextbox for searching another string",doDBSearch,this.value);" ...

haves two different event queues.

enjoy! innovate!

PS.

Original article and technique is bring from an article of Peter Higgins, a Dojo guru http://dojocampus.org/content/2009/09/28/rate-limiting-with-javascript/

Rate Limiting with JavaScript
by Peter Higgins

Sometimes you need to be alerted when some event or action happens, but the event or action could happen multiple times in quick succession. A perfect example of this is window.onscroll. The window.onscroll event fires entirely too much. Not only that, it fires inconsistently across browsers. This has been talked about before. Here, I offer a solution:

We can make perfect use of some JavaScript built-ins: setTimeout and clearTimeout, and some uber-cool Dojo magic: dojo.publish and dojo.subscribe. PubSub is a mechanism for arbitrary communication between elements. The usage pattern fits here perfectly. Because window.onscroll fires so much, having multiple connections to this event can cause serious slowdowns in your application when the user scrolls. This technique involves connecting to window.onscroll once, and rate-limiting the firing of this event to something more manageable.

Start by making the connection. We’ll wrap it in an anonymous-self-executing function to scope our variables and keep them out of the global space:

(function(d){
var timer, // create a variable to store a timeout
rate = 50 //ms .. and a variable to use for the delay
;

// setup one connection
d.connect(d.global, "onscroll", function(e){
// if this function has been previously called and not fired,
// clear the timeout
timer &amp;&amp; clearTimeout(timer);
timer = setTimeout(function(){
// publish a custom topic when this timeout executes
d.publish("/window/scrolled", [e]);
timer = null;
}, rate);
});

})(dojo);

If the onscroll event fires 100 times in a short period, only the last occurrence will fire the publish. We can utilize this rate-limited version of onscroll simply by subscribing to the “/window/scrolled” topic.

dojo.subscribe("/window/scrolled", function(e){
// do some recalculation based on knowing the scrolling is "done".
// will fire at MOST once per 50ms, so won't be very expensive
});
This technique can be applied to most anything. Perhaps you have a button which sends an Ajax request. Some users double and triple-click buttons out of habit, inadequate visual feedback or a number of other reasons. We can ensure that only one click is allowed within a rate limited window. Same basic setup, different event. The original connecting code might look something like this:

(function(d){

// this sends our POST based on whatever form
var someFunction = function(e){
dojo.xhrPost({ form:"someFormId" });
}

// setup the click events.
d.query(".buttons").onclick(someFunction);

})(dojo);
If a user clicks something with class=”buttons” rapidly in succession, `someFunction` will be called that many times, causing
many Ajax requests to be sent. Converting the code to something which will only fire once per `rate` ms is

(function(d){

var someFunction = function(e){
dojo.xhrPost({ form:"someFormId" });
}

var timer, rate = 50;
dojo.query(".buttons").onclick(function(e){
timer &amp;&amp; clearTimeout(timer);
timer = setTimeout(function(){
someFunction({ target: e.target });
timer = null;
}, rate);
});

})(dojo);

Getting a little more advanced, we can take this concept and reduce it to a common function. We’ll invent a new API to handle all the rate-limiting locally. We’ll just make a function which accepts an extra parameter for the rate to set:


(function(d, nl){

d.connectLimited = function(rate, target, event, scope, cb, fixDom){
// summary: Just like `dojo.connect`, but takes an additional argument BEFORE
//		standard connect() arguments: rate. This value is used to prevent
//		rapid successive calls to this event

var timer,
fn = scope &amp;&amp; cb ? d.hitch(scope, cb) : scope
;

return d.connect(target, event, function(e){
timer &amp;&amp; clearTimeout(timer);
var args = arguments;
timer = setTimeout(function(){
fn.apply(d, args);
timer = null;
}, rate);
}, null, fixDom);

}

nl.prototype.connectLimited = function(rate, event, scope, cb, fixDom){
// not a straight forEach, need to shift `node` into each call
return this.forEach(function(node){
d.connectLimited.call(d, rate, node, event, scope, cb, fixDom);
});
}

})(dojo, dojo.NodeList);

By returning the handle from the rate-limited dojo.connect call, we are able to disconnect this event with dojo.disconnect.

Now, to use it we simply call the function passing a rate in addition to whatever we normally would have called:


dojo.query(".buttons").connectLimited(50, "onclick", someFunction);
// or
dojo.connectLimited(75, window, "onscroll", function(){
dojo.publish("/window/scrolled");
});

Hope this helps someone.

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , , , , , , , ,

HTML5 will kill Flash? Some impressive demos and experiments.

Take a look at these HTML5 Canvas experiments and fall in love (No flash, no plugins, just HTML5 and Javascript):

http://www.chromeexperiments.com/detail/browser-ball/

http://www.chromeexperiments.com/detail/ball-pool/

ball pool screenshot

http://www.chromeexperiments.com/detail/harmony/

a lot more on: http://www.chromeexperiments.com/

Bye Bye Flash,

Bye Bye Javascript haters…..!

Bye Bye stupid extension like this http://noscript.net/ (some people use it feeling so nerd)

Of course the suggested browser to let you see these HTML5 demos working well is the awesome Internet Explorer 6 :

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Using Redis in PHP to create shared variables

One of the most hateful thing in php, in my opinion, is the absence of cross-script variables, i mean a sort of $_APPLICATION instead of $_SESSION, i mean real static variables eg. a counter, a lock, a queue to serve a multi-process elaboration etc.

Redis is an in memory key-value db, and with its php library Rediska (all opensoruce) can help a lot to emulate this efficiently so to power up your php applications and to save your time (surely is better and easier than using MySQL or Files for this purpose)

e.g.

// Init
require_once 'Rediska/Key/List.php';
$List = new Rediska_List('names');

// Set
$List-&gt;append('kevin');
$List[] = 'john'; // Also works

// Get (this could be done at any time, by any process,
// just initialize the List again)
foreach ($List as $name) {
    echo $name . "n";
}

More info on this blog article:

http://kevin.vanzonneveld.net/techblog/article/redis_in_php/

Reblog this post [with Zemanta]
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , , ,

Opensolaris – How Install the adobe flash player plugin into firefox

Adobe Flash Player
Image via Wikipedia

Opensolaris - Songbird Wallpaper
Image by maccu via Flickr

Installing the flash plugin in firefox under opensolaris at first approach can appear an hard  operation (cause the failure of the firefox plugin install wizard) but  actually is a really simple task:

1- Download the solaris version of the adobe flash player plugin from the official adobe site

2- Just copy the .so file into the firefox plugin directory

su
cp ~/Downloads/FlashPlayer/libflashplayer.so /usr/lib/firefox/plugins/.

now just restart firefox and enjoy!

Reblog this post [with Zemanta]
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , ,

Monitoring Ajax application usage and usability with Google Analytics Event Tracking APIs

I have an ajax application or website, what’s the best way to monitoring user interaction?

Google Analytics! (you can use it also if your webapp is an intranet app: GA API sends data to google server from javascript! Then you need just that your application user got an internet access)

How to use Google Analyitics API to log dynamic  page events?

Google introduced the awesome event tracking feature in Google Analytics API, so using the gaTracker object is very very easy

for instance:


<script>

var loggedUsername='<?php echo $_SESSION['<a class="zem_slink" href="http://en.wikipedia.org/wiki/User_%28computing%29" title="User (computing)" rel="wikipedia">username</a>']; ?&gt;'

function zoom(img){

logGASingleEvent('gallery','image_zoom',loggedUsername);

//other stuff

}

function logGAEvent(category,action,label){
     pageTracker._trackEvent(category,action,label,1);
}

</script>

echo '<img onclick='zoom(\"$gallery_img\");'  ="" src="$gallery_img">;';

logs a user click on an img tag  (in this case the javascrip t zoom() function related to this onClick DOM event).

Ps. i like to put username in label for having a final report like this (a per user action overview):

per user action report

…and this is the general event overview:

analytics event tracking report overview

Isn’t cool? Don’t you love Google?

See also:

Using GA Event API to Monitoring Website Performance

Reblog this post [with Zemanta]
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , , , , , , , , , , , , , , ,

Browser Package – All major browser in a pacakge for testing your webapp

Need a test environment to test your DHTML webapp?

This kit is very useful: it’s based on a vmware virtual application packaging technology and gives to you a clean and standalone installation of all major browser, including:

- Firefox 3.5.6 (updated)
- Firefox 3.6 beta 5 (updated)
- IETester 0.4.2
- Netscape 9.0.0.6
- Opera 10.10
- Opera 10.20 Test (added)
- Safari 4.04 (updated
- Chrome Google 3.0.195.33
- Chrome Google 4.0.223.16 beta
- Maxthon 2.5.11 (updated)
- Maxthon 3.0.4.8 Alpha
- Arora 0.10
- SeaMonkey 2.0

Installations are standalone and based on virtual machines then this package is not invasive for your windows register.

(Yes i need to use Windows, but just because i need to test my webapps in all existing world browsers…)

Download it here (the site is in french):

http://labs.xoofoo.org/modules/news/

http://tutos.xoofoo.org/modules/mydownloads/visit.php?cid=23&lid=96

Reblog this post [with Zemanta]
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: ,

Tip: Internet Explorer DIV Height Problem

Yes i know, i’m for GWT, Ext, Dojo, Zk, and all the other frameworks that made life easy to web developers abstracting the HTML/CSS and most part of the Js layers, but I come from hard and deep JS/DHTML/CSS hand-programming (HttpXMLRequest sweet HttpXMLRequest) then somethimes i help my collegues to solve classic HTML/Js problems…

One of this is the Interent Explorer buggy way of manage div heights: Any height less than 20px is rendered as 20px.

I just give to you some techniques to hack this annoying IE bug:

1) Put a comment inside an empty div:

<div style="height: 10px;"><!-- --></div>

2) Put a &nbsp;  inside the div and add this to its style: font-size:1px;
line-height:0.

<div style="font-size: 1px; line-height: 0pt;">&nbsp;</div>

of course the styling rules can be put in a css class…

Refs:

http://archivist.incutio.com/viewlist/css-discuss/39150

http://www.codingforums.com/showthread.php?t=46408

Reblog this post [with Zemanta]
Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , ,

How to open a clothes shop and become a stylist in 5 minutes (and with a 0$ budget)

Hi guys, i just opened a new international clothes shop and started my stylist career. Zero investment, zero burocracy:

http://creativebrains.spreadshirt.it

Are you a creative mind? your dream is to see your creation all over the world like “Che Guevara” shirts?

Try to open your clothes shop on spreadshirt.com, you can  create clothes and gain money selling it… it’s free!

http://www.spreadshirt.com

PS. if you like it, please buy a nerd/creative shirt to support creativeprogramming.it:



(life is short, enjoy it!)

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , , , ,

Using Google Docs as a PDF templating engine

You need to replace text strings in an existing document template?

..and you need to get the output in PDF format?

How to achieve this?

a PDF API?  an Open Office one? or a Microsoft Office “pay for nightmare” SDK (Suicide Developer Kit) ?

nooo… the (easy) solution is Google Docs…!

In fact, thanks to the Google Document List API and the Google Data Protocol, you can edit your document (replacing strings in the template) via an update of the HTML body of the Google Doc Format. I said HTML body, yes , not Postscript-like formats or hard binary data…

It’s official: I love Google.

This is little  demo screencast, enjoy:  (is a PDF2FAX application written for my enterprise.)

Screencasts and videos online


How to do this?

It’s very simple let’s see with a quick and rough tutorial (You know, there’s very little time to blog when you work as a developer, but you know also that working examples are the best and quick way to learn)

1) Download Google Data APIs for Java

http://code.google.com/p/gdata-java-client/

2) Download Google Data APIs Samples

http://code.google.com/p/gdata-java-client/downloads/detail?name=gdata-samples.java-1.41.3.zip&can=2&q=

3) Extract the archives and find for DocumentList.java example file provided by google

4) Add these methods in:


  /**
     *
     * Does replacements into a google doc document and gets it converted in PDF format (nor return value, saves file to disk)
     *
     * @param docResourceId  the id of the google doc template
     * @param newDocName the name to assign to elaborated copy of the doc (on google docs)
     * @param replacementsMap replacements pairs eg. "$$name" -> "Stefano"
     * @param filename filename including path and excluding .pdf excension where saving resulting PDF e.g. "C:\\PDFs\myReplacedDoc" or "/var/www/newdoc"
     */
    public void doReplacementsInGoogleDocTemplateAndSavePdfFileToDisk(String docResourceId, HashMap<String, String> replacementsMap, String filename) {
        try {
            String ext = "doc";  //the format of temporary download to be reuploaded (is a trick to copy the template google doc, java api doesn't support operation yet)  TODO: avoid this when APIs will support copy operation (to get 3x speed improvement of this method)
            String file = filename + ext;
            downloadDocument(docResourceId, file, ext);
            DocumentListEntry copy = uploadFile(file, filename.substring(filename.lastIndexOf(Commons.getCommons().FILE_SEPARATOR) + 1 + docResourceId.length()) + "_" + ext + "_" + System.currentTimeMillis());
            String content = downloadFileNoSave(
                    new URL(
                    ((MediaContent) copy.getContent()).getUri()));

            for (String k : replacementsMap.keySet()) {
                content = content.replace(k, replacementsMap.get(k));
            }

            /*
            google docs broken image src fix  //TODO: verify if always needed (temporary gdoc bug?)
            eg.  <img alt="" height="61" src="ddztq9dq_79d7k4qqr3_b.png" style="BORDER:none" width="161">
            to
            <img alt="" height="61" src="File?id=ddztq9dq_21dn2cs4gt_b" style="border:none" width="161">
            */

            content = GenericUtils.regexpReplaceAll("(\\<img.*src=)([^\\.]+)[^\\s]+([^\\>]+\\>)", "$1" + "\"File?id=" + "$2" + "\"" + "$3", content);

            copy.setMediaSource(new MediaByteArraySource(content.getBytes(), "text/html"));

            DocumentListEntry updatedEntry = service.updateMedia(
                    new URL(copy.getMediaEditLink().getHref()), copy);

            //  Logger.getAnonymousLogger().log(Level.INFO,"New doc body: "+content);
            ext = "pdf";
            file = filename + ext;
            downloadDocument(copy.getResourceId(), file, ext);
            Logger.getAnonymousLogger().log(Level.INFO, "GDOCs Operation Success");
            //copy.updateMedia(true);
        } catch (Exception ex) {
            Logger.getLogger(DocumentList.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
   /**
     *
     * @param exportUrl
     * @return file content
     * @throws IOException
     * @throws MalformedURLException
     * @throws ServiceException
     * @throws DocumentListException
     */
    public String downloadFileNoSave(URL exportUrl) throws IOException,
            MalformedURLException, ServiceException, DocumentListException {
        if (exportUrl == null) {
            throw new DocumentListException("null passed in for required parameters");
        }

        MediaContent mc = new MediaContent();
        mc.setUri(exportUrl.toString());
        MediaSource ms = service.getMedia(mc);

        InputStream inStream = null;
        StringBuffer buff = new StringBuffer("");
        try {
            inStream = ms.getInputStream();
            //outStream = new BufferedWriter(fstream);
            int c;
            while ((c = inStream.read()) != -1) {
                buff.append((char) c);
            }
        } finally {
            if (inStream != null) {
                inStream.close();
            }
            return buff.toString();
        }
    }
public class GenericUtils {

    public static boolean regexpContains(String regex, String string) {
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
        Matcher m = p.matcher(string);
        return m.find();
    }

    public static String regexpReplaceAll(String regex, String replacement, String string) {
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE );
        Matcher m = p.matcher(string);
        return m.replaceAll(replacement);
    }

      public static String regexpReplaceAllMultiline(String regex, String replacement, String string) {
        Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
        Matcher m = p.matcher(string);
        return m.replaceAll(replacement);
    }
}

5)Test it from DocumentListDemo.java so adapt it to fit your application need

  DocumentListDemo client = new DocumentListDemo(System.out, APPLICATION_NAME,
        authProtocol, authHost, protocol, host);

    if (password != null) {
      client.login(user, password);
    } else {
      client.login(authSub);
    }

     client.documentList.copyUpdateAndGetPdfOfDocument(templateResourceId, "uniqueName_"+System.currentTimeMillis(),replacementsMap,filename);

6) That’s all! be creative can be smooth: you have just saved a lot of developing time and also CPU of your servers.

Enjoy and please support creativeprogramming.it click on google friends connect button, share this article on buzz, tweeter etc, like i share knowledge with you (open source really matters)

for any webapps or website realization contact me at: info@creativeprogramming.it or via my LinkedIn profile

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Blogplay
  • Add to favorites
  • Diggita
  • DZone
  • HackerNews
  • LinkedIn
  • PDF
  • Ping.fm
  • Reddit
  • RSS
  • Segnalo
  • Technorati
  • Tumblr
  • Twitter
  • Wikio
  • SphereIt
  • Suggest to Techmeme via Twitter

Tags: , , , , , , , , ,