Proxymity's Forum Posts

  • There is an issue with my router or wifi preventing me from previewing.

    Is there a way to preview by plugging my phone into my laptop?

    What mobile OS do you have? iOS, Android, WP, ... - and what OS is on your laptop?

    Depending on which constellation of OSs you got, normally theres always a way to build a tethered connection between your phone and your laptop/computer (not sure about Samsung, Sony, ... devices - they don't even know what they are doing on mobile market - so that feature may be disabled without rooting it).

    If you got your tethered connection built up via micro-USB/Lightning/whatever-cable, run 'ipconfig'/'ifconfig'/whatever-command-outputs-your-computers-IP on your desktop/laptop and write down the IP dedicated to the tethered network (not likely the 192.168.x.x- address which is normally your home-network). Enter that IP on your mobiles browser while its beeing tethered and see what happens.

    This will be a direct-access onto your device. Maybe you also have to adjust the selected port on your desktops/laptops firewall (if any is configured). Keep in mind that this is _not_ comparable to a WAN-remote-connection - some things might not work - or will work but later not.

    Best practice for you could be possible resetting your home-router and reconfiguring it properly.

    Have a great day,

    Proxy

    # Edit: if you are running Windows 10 on your laptop: go to Settings (the metro-app-ish one) and enter "Hotspot" in the search bar. There you got the menu "Change mobile hotspot settings" - just hit the on/off switch to activate an ad-hoc network from your laptop (if your WIFI-card supports this!). SSID and its password are displayed in the middle of the page (to change those you have to disable the hotspot and re-enable it after doing changes). This is possibly the easiest way.

    You might want to take a look into this PlugIn:

    https://readymag.com/valerypopoff/valerypopoff-js-plugin/

    Using the PlugIn will make your life easier when it comes to pre-provisioned *.JS files.

    Depending on what kind of 'app' - or let's say: on what platform & architecture the final 'app' should run, I can recommend you using NodeJS for that project. Take a look at the CLI 'zeit/pkg' (https://github.com/zeit/pkg) which puts your website into a chromium-engine powered 'container' and creates an executable out of it.

    The easier and friendlier way would be 'Electron': https://electronjs.org - also check this one out.

    Have a great day,

    Proxy

  • Hi risto,

    you possibly could mix NWjs in ( https://www.scirra.com/manual/162/nwjs ) which is capable of writing data to a local (or remote-connected) storage medium. Using NWjs would force you to get away from AJAX write operations since it only reads data in this constellation.

    Otherwise you could setup a local dev-environment, using your local Node/PHP/whatever back-end for debugging purposes (depending on "what" you want to achieve in your prod-environment). There are some toolkits which feature out-of-the-box configurations (depending on your OS).

    Haven't worked for quite some time with C2, but I think just throwing out a *.JSON (or of course *.CSV or *.TXT or [...]) would be the easiest option since vanilla HTML is capable of saving files (in other words: ready up the content to be saved and trigger a file-download).

    Have a great day,

    Proxy

  • Cheers RiddleQs,

    check this post:

    Have a great day

    Proxy

    #edit: also this one could come in handy:

  • Hi!

    Hmm if I understand it right, you could possibly:

    Create a "default" sprite for your player (the dot) and an additional sprite "spike". On collision with an another object, you could "pin" (behavior) the sprite "spike" on your "dot"s position, using its imagepoint and setting up an angle. Should work fine.

    Have a great day

    Proxy

  • Brilliant, thanks for the detailed and informative reply!

    No prob, helping out, whenever I can <img src="{SMILIES_PATH}/icon_e_wink.gif" alt=";)" title="Wink">

    For the backoffice probably ill use a separate grails based system + mysql as you can develop an enitre backoffice in half a day, and mysql is a better option for finanical transactions due to its referential integrety.

    You can also make usage of multiple databases (and even use SQL and NoSQL together); there are many plugins for that.

    The only issue I have found, without a solution yet, is now to have a clustered multiplayer object on a single server. I.e. if you use the node cluster feature, it creates multuple node child processes using fork. If I have a player connected with a socket in one child process, he cant access the mulitplayer game instance running on another child process.

    Thanks for explaining me a problem, I never encountered before (never needed to pass handshakes before). A quick research at the plugin of my trusts docs:

    1. Get pm2 asap (a great process manager for node), if not already done: http://pm2.keymetrics.io

    2. Learn its usage (done in 5 minutes): http://pm2.keymetrics.io/docs/usage/quick-start/#usage

    3. Just start your node server again clustered, with using pm2

    Some reported, that it works (when they got problems without using pm2, like you had), some still have issues (https://github.com/Unitech/pm2/issues/389).

    Since I'll encounter this problem too, at some point, I'll investigate it later. Since my current project has at least 2 different backends, I possibly could (if no solution fits) make a 3th one, which is exclusively used for communication between clients and servers and simply forwards over data between backends.

    However, Ill checkout colyseus first - nice one!

    It's a great product, but lacks on documentation somewhat. By downloading it, there arent really things like broadcasts (to all clients) included on its node side (you have to implement those by yourself) - if you need help or have questions about it, feel free to ask.

    Have a great day,

    Proxy

  • Hi nutmix,

    tl;dr: use NodeJS - never ever even think about some other way (incl. services like Photon, etc.; Java, PHP, neighbors cat or whatever comes into mind) to realize a real-time-multiplayer backend! Just don't (speaking of the "state-of-the-art" in 2017)! If you ignore my suggest and do, you'll regret it some day as bad as possible.

    if you have the knowledge and ressources to eigther get a hosting service or host a server yourself (a virtual one, because a dedicated one would be an overkill for this) (think about security, etc.!), then I'ld suggest you in any case the usage of NodeJS.

    Not only, that you can literally do anything with node; its powerful, super fast (heres a comparison with Apache, for example) and scalable untill you go nuts.

    You need a database for your project/Node backend? Download MongoDB (its free), and create a RESTful api (commonly "express" (routing/http middleware) or a CRUD interface will let your backend instantly turn into something dynamic, accepting requests from the outside (clients, ...). Combined with, e.g. "mongoos" (ORM mapped gateway/interface for MongoDB, which makes easy stuff even more easyier - like creating schemes for your tables, etc.), you then will have everything you need, nearly out of the box.

    Great thing about node is, that there are many tutorials... Ild suggest you to start with "best practices" for node in general; this will show you how node works and where you have to pay attention in your code (especially regarding performance and stuff). With some little tweeks, a node server can be a powerfull backend for everything, regardeless if you communicate over http/https or with sockets, etc.

    Colyseus is, for example, a great open source framework (not only for games): http://gamestd.io/colyseus/ (git: https://github.com/gamestdio/colyseus). Theres also a plugin for Construct 2 and a documentation for it (the dev also got a channel on slack, if there are any problems/questions). Btw: I'm also using Colyseus for my current project - its one of the best frameworks (when it comes to real time communication (its based on/using socket.io - https://socket.io/) I've discovered so far.

    Colyseus only offers websocket communications - if you, for example, also need a http-support (or web-frontend in general), you could implement your own middleware or use an another framework (like: total.js (https://docs.totaljs.com/latest/en.html ... %20started) - its also great, fast, open and supports websockets, as well as ajax, etc.)... important (at least I'm really suggesting it): use a socket-based communication for your clients. With the help of a REST api (AJAX), a real-time multiplayer game is also possible, but sockets are mind-blowing fast and easy to use.

    Within the past years Ive nearly forgotten the existence of any other language beside JS, because node is the only thing I'm using - because it covers literally anything (I need).

    2. node.js

    c) very basic DB support?

    NodeJS itself has no DB support, correct. But with a middleware/driver (

    Starting from local plain data files like csv or binary NoSQL (serversided - and clientsided, if you got an api server or something) up to SQL (MSSQL, MySQL,...) and NoSQL (MongoDB, Postgre,...) - or your very own developed database type/server, anything can be connected and used in your NodeJS application/project.

    For example: searching MSSQL interface? Click here: https://www.npmjs.com/search?q=mssql&pa ... ng=optimal

    2. node.js

    d) no backoffice UI support.

    Same as above: NodeJS only got its console by stock, where you can console.log('I am a white line of printed text').

    Depending on what kind of UI you want to have:

    • WebUI: use "express" as middleware for the web; create a login routine for admins/supporter, create your HTML files for admin UI, route them with express (taking care of auth-token or similar within routing) and you got your admin WebUI
    • Want to write your UI in any other language? Then do the same as above, without HTML files, create an api (RESTful, or a CRUD support, etc.) and let anything speak with your backend, anytime, anywhere (need cors? Also no problem, express features it out of the box)
    • Even if you need some other communication between backend (or db, or whatever) and backoffic - I'm sure that there is at least one (simple) way of doing so

    Also, same as above, take a look here: https://www.npmjs.com/search?q=backoffi ... ng=optimal

    2. node.js

    e) researching this, it seems node is single threaded. Thus critical game timers would wait on the same event queue as incomming messages. Nodes "solution" to this is to cluster addtional forked worker processes. Inter-process communication between these forked processes goes through the parent "master" in the form of messages, which is not scalable. The general solution is to have another layer of servers with the game logic, and use RPC or sockets to connect the "front end" node processes with backend game logic servers. Checkout pomelo for an example of this https://github.com/NetEase/pomelo/wiki/ ... k-overview

    Again, same as above (what suprise <img src="{SMILIES_PATH}/icon_e_biggrin.gif" alt=":D" title="Very Happy">):

    Vanilla NodeJS is always single threaded. But... (I think you know what follows), there are (more than one) solutions.

    NodeJS already has a built in cluster-feature (docs: https://nodejs.org/api/cluster.html#clu ... w_it_works) with which you can, e.g., create a highly scalable webserver (Tutorial: https://www.sitepoint.com/how-to-create ... your-apps/).

    You could also use development/cli plugins, for, e.g. running multiple instances paralell, which load-balances themselves, etc... there are than enough ways.

    Even the ablity to make JavaScript asynchronious is possible...

    With the depency "pug" you can use the HTML-template engine Jade (do I have to say "out of the box" again?) for your whole Web-frontend (or parts - or just single site or a single url), ...

    How I said earlier: for me, NodeJS covers anything I need - and is the "type of server" I recommended everyone who asked in the past years.

    Hope that I helped you out a bit - if I wrote bullshit on some part (not readable/understandable/unclear) or if you got more questions which I possibly could answer, just answer back here or write me a pm - I'll try my best! <img src="{SMILIES_PATH}/icon_e_wink.gif" alt=";)" title="Wink">

    Have a great day,

    Proxy

    Edit: for my current project I created two backend servers, "logic" and "storage", which are communicating together. If a client requests for example something out of the database, the request is routed:

    Client -------request-------> backend(logic) -------request-------> backend(storage) -------request-------> MongoDB

    and the according response is delivered:

    MongoDB ------response-----> backend(storage) ------response-----> Client

    backend(storage) has the chance to talk directly with the client, when backend(logic) also transfered its ID to the backend(storage) - so there is no impact on backend(logic) for DB-related requests - as long as there is no data-processing needed (motion sync, calculating "damage done" after attacks, etc. is routed over the backend(logic) - otherwise there would be no "cheat/inject protection", etc..

    Just for giving you a small example - hopefully its understandable what I wrote (my english isnt the best <img src="{SMILIES_PATH}/icon_e_biggrin.gif" alt=":D" title="Very Happy">).

  • Hi again ;D

    1. If we open "C2" JSON can we call the contents between {"c2array":true,"size":[2,2,2],"data": and } a "real" JSON?

    [[["1","Hello"],["3","Array"]],[["2","World"],["4","Editor"]]][/code:pzgkwhjj]
    

    Well, as I said, I havent worked with C2 for a few months now - maybe I'm wrong - Ill check that out if I find time for that

    2. Well, can you use Android version or do you need exactly HTML5 version?

    Even worse: iOS (company devices - I'm also using them) .

    Cheers,

    Proxy

  • Cheers PaulPoy!

    This was one of those "awfull" moments, for me, when you realize, that something, you worked on for hours, and still do, basically already exists.

    Search functions in forums are a great feature......

    Thanks!,

    have a great day,

    Proxy

  • Cheers!

    Great tool, editing an array is, compared to existing ones found in forums, something fun!

    Some questions:

      I think, that "save" saves the array in a C2 compatible format - its not really that hard to convert it to a "real" JSON (maybe I'm wrong, havent worked with C2 for quite a while - but the syntax of a "C2 JSON" differs on some points, compared to a "real JSON"), but it could be possible to also give the user that option?
      Could it be possible to provide a web based app (as a html project)? Maybe as a separat purchase/article in store or with your credits in the splash and on the bottom line or something.. since I'm working on mobile devices from time to time, having such would be nice. The app could be published by anyone for anything, that might be the deal breaker, but.. well.. maybe theres some way ;D

    Have a great day,

    Proxy

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Hi!

    Im seeking a dev for, let me shorten it up: a character "barebone". Means: I need a universal "character-template" which can be costumized by the player by given tags, such as: if attackrange=X, then the final character needs to be X pixels away from the target to attack. Or things like costumizable attributes (if character buys skill XY in shop, then highen its attack/lower cooldown, etc.).

    I roughly know how to achieve this, but it would be great to get a "well coded" template for that (regarding performance and stuff).

    All characters, which are created out of this template every round, are beeing controlled by players (sidescroller, versus game) - so no AI (or basic elements to enable it) is needed.

    Does someone can PM me with details - or got a simple tutorial for this? This seems the best way (for me) to achieve a character selection with less code (to name some gamed: Leagues of Legends, World of Warcraft or any other game where a character meets different classes... dont want to write the whole code for every possible character... taking a "template system"would be a better deal ;D).

    Best regards,

    Proxy

  • Try:

    NWjs | Run "start" for a CMD and

    NWjs | Run "start pathToBatFile" for a batch-file

    for example:

    NWjs | Run "start c:\mybat\batch.bat"

    You, sir, are a hero! <img src="{SMILIES_PATH}/icon_mrgreen.gif" alt=":mrgreen:" title="Mr. Green">

    Thanks! Works totally fine.

    [quote:1xceka4q]Run file

    Run the file at an existing file path. This is analogous to double-clicking the file in the OS file explorer. For executable programs, it will attempt to run the program; for other file types, it will open the default associated program, such as the system default image editor if an image file is given.

    https://www.scirra.com/manual/search?q=nwjs

    Would be worth mentioning there Ashley ;D

    Have a great weekend

    Proxy

  • I hope this will work for you (I've not tried it). NWjs spawning .bat and .cmd

    Edit:

    If you include the .bat file inside the package.nw (ie import it into c2) then use:

    require('child_process').exec('cmd /c batfile.bat', function(){
       // …your callback code may run here…
    });[/code:192a6lsf]
    

    Thanks, Colludium!

    Already took a look at that Wiki entry - but I hoped to realize that without modifing anything.

    I could also use ajax to realize that, but the main problem is, that I do not want to add anything into the C2 project. When starting the script/app you are prompted for 2 *.csv files, one contains software (vendor,name,path-to-exe, etc.) and one contains a collection of batch-files. The reason why Im doing it that way: not every machine has the same software and/or "functions/features" installed - because of that, you ll have multiple "Softwareconfiguration.csv"s and "Functionconfiguration.csv"s. Plus: when a new software is added to the system, you only have to add a row in one of the *.csv file; same with the "function" configuration. Its aimed to be multiusable without even thouching the C2 projectfiles... (you may know the wise words: "lazy admin is best admin" ).

    So all in all I want to compile the C2 project a single time and want to be able to extend the functionality anytime just by editing *.csv files (and last but not least, editing a *.csv file is much more easier for my collegues instead of opening C2 and editing something in the project itself (and of course, I own the license by myself, not in/for the company ).

    But well, if there isnt an another way, I have to mod the plugin.

    Thanks, anyway!

    Have a great weekend!

    Proxy

  • Hi community!

    First off: this question isnt part of a game - Im playing arround with C2. I want to extend a console-automation I wrote for my company with a GUI (since making a GUI in PowerShell is a pain in the a.... without tools from SAPIEN or others...).

    I tryed a whole lot in making NWjs to open a *.bat or command prompt window. I can't figure out how to do that.

    Something of what I tried so far:

    • NWjs | Run "C:\Temp\my_bat_file.bat" // nope
    • NWjs | Run "C:\Windows\system32\cmd.exe" // nope
    • NWjs | Run "C:\Windows\SysWOW64\cmd.exe" // nope
    • NWjs | Run "cmd.exe" // nope
    • NWjs | Run "process.env.ComSpec + ' /c batfile.bat" // nope (whyever I thought that may works - found that while searching on Google - and yes, I pasted node.js code here... )

    Since launching a *.exe works fine just as it should, I downloaded a bat2exe complier and compiled my *.bat into an executable:

    • NWjs | Run "C:\Temp\my_bat_file.exe" // also: nope

    [everything else what I tried doesnt really made sense at all...]

    My batch just pings Google (8.8.8.8) so far - nothing special. I do not need any kind of automation/value-returning - the user should just see the opened command prompt window with its contents shown before a "pause"- interaction.

    I thought about security reasons why it isnt possible - but writing a malicious script in VS/vbs and compiling it into a native executable is just as easy as in a simple batch script - so I dont think that someone restricted that because of security reasons (obviously I do not want to write malicious software nor harm any system - just to have it said at this point!).

    Does someone know a work-arround for simply opening a *.bat - or if not otherwise possible a compiled *.exe from a *.bat script?

    Have a great weekend

    Proxy

    Edit: even the fail-safe method: NWjs Open Dialog, choosing file, Run NWjs.ChosenPath - doesnt work... I think this has nothing to do with a wrong path or such...

  • andykenobi

    *Sorry for the delay.

    [quote:3oll0gef]

    • Add Set Header (*not stable with fixed header).
    • Add Change Value.

    edittime

    function GetPluginSettings() 
    {
    	return {
    		"name":		"Listview",
    		"id":			"Listview",
    		"version":		"1.3.2",
    		"version modified":		"2",
    		"description":	"Listview.",
    		"author":		"HMMG",
    		"modified by":	"PlayLive",
    		"help url":		"https://www.scirra.com/forum/plugin-listview-header-sorting_t124360",
    		"category":	"Addon",
    		"type":		"world",			// appears in layout
    		"rotatable":	false,
    		"flags":		pf_position_aces | pf_size_aces,
    		"dependency":"jquery.stickytableheaders.min.js;jquery.tablesorter.js;"+
    						"theme.black-ice.css;"+
    						"theme.blue.css;"+
    						"theme.bootstrap.css;"+
    						"theme.bootstrap_2.css;"+
    						"theme.dark.css;"+
    						"theme.default.css;"+
    						"theme.dropbox.css;"+
    						"theme.green.css;"+
    						"theme.grey.css;"+
    						"theme.ice.css;"+
    						"theme.jui.css;"
    	};
    };
    
    ////////////////////////////////////////
    // Parameter types:
    // AddNumberParam(label, description [, initial_string = "0"])			// a number
    // AddStringParam(label, description [, initial_string = "\"\""])		// a string
    // AddAnyTypeParam(label, description [, initial_string = "0"])			// accepts either a number or string
    // AddCmpParam(label, description)										// combo with equal, not equal, less, etc.
    // AddComboParamOption(text)											// (repeat before "AddComboParam" to add combo items)
    // AddComboParam(label, description [, initial_selection = 0])			// a dropdown list parameter
    // AddObjectParam(label, description)									// a button to click and pick an object type
    // AddLayerParam(label, description)									// accepts either a layer number or name (string)
    // AddLayoutParam(label, description)									// a dropdown list with all project layouts
    // AddKeybParam(label, description)										// a button to click and press a key (returns a VK)
    // AddAnimationParam(label, description)								// a string intended to specify an animation name
    // AddAudioFileParam(label, description)								// a dropdown list with all imported project audio files
    
    ////////////////////////////////////////
    // Conditions
    
    // AddCondition(id,					// any positive integer to uniquely identify this condition
    //				flags,				// (see docs) cf_none, cf_trigger, cf_fake_trigger, cf_static, cf_not_invertible,
    //									// cf_deprecated, cf_incompatible_with_triggers, cf_looping
    //				list_name,			// appears in event wizard list
    //				category,			// category in event wizard list
    //				display_str,		// as appears in event sheet - use {0}, {1} for parameters and also <b></b>, <i></i>
    //				description,		// appears in event wizard dialog when selected
    //				script_name);		// corresponding runtime function name
    				
    
    AddCondition(0, cf_trigger, "On selection changed", "Listview", "On selection changed", "Triggered when the selected ligne changes.", "OnSelectionChanged");
    AddCondition(1, cf_trigger, "On clicked", "Listview", "On clicked", "Triggered when the Listview is clicked.", "OnClicked");
    AddCondition(2, cf_trigger, "On double-clicked", "Listview", "On double-clicked", "Triggered when the Listview is double-clicked.", "OnDoubleClicked");
    AddCondition(3, cf_trigger, "On mouse over", "Listview", "On mouse over", "Triggered when the cursor is over the Listview .", "OnHover");
    
    ////////////////////////////////////////
    // Actions
    
    // AddAction(id,				// any positive integer to uniquely identify this action
    //			 flags,				// (see docs) af_none, af_deprecated
    //			 list_name,			// appears in event wizard list
    //			 category,			// category in event wizard list
    //			 display_str,		// as appears in event sheet - use {0}, {1} for parameters and also <b></b>, <i></i>
    //			 description,		// appears in event wizard dialog when selected
    //			 script_name);		// corresponding runtime function name
    
    AddNumberParam("Index", "The zero-based index of the Row to select.");
    AddAction(0, af_none, "Set selected index", "Row", "Select Row <i>{0}</i>", "Select a Row in the listview.", "SelectedRow");
    
    AddComboParamOption("Invisible");
    AddComboParamOption("Visible");
    AddComboParam("Visibility", "Choose whether to hide or show the listview.");
    AddAction(1, af_none, "Set visible", "Appearance", "Set <b>{0}</b>", "Hide or show the listview.", "SetVisible");
    
    AddAction(2, af_none, "Set focused", "Listview", "Set focused", "Set the input focus to the listview.", "SetFocus");
    AddAction(3, af_none, "Set unfocused", "Listview", "Set unfocused", "Remove the input focus from the listview.", "SetBlur");
    
    AddStringParam("Text", "The Row text to add (to add \":\" use \\:). Exemple 3 Column       \"Item1:Item2:Item3\"");
    AddAction(4, af_none, "Add Row", "Row", "Add Row <i>{0}</i>", "Append a new Row to the listview.", "AddRow");
    
    AddNumberParam("Index", "The zero-based index of the Row to insert before.");
    AddStringParam("Text", "The Row text to add (to add \":\" use \\:). Exemple 3 Column       \"Item1:Item2:Item3\"");
    AddAction(5, af_none, "Add Row at", "Row", "Add Row <i>{1}</i> at index <i>{0}</i>", "Append a new Row to a specific place in the listview.", "AddRowAt");
    
    AddNumberParam("Index", "The zero-based index of the Row to remove.");
    AddAction(6, af_none, "Remove At", "Row", "Remove Row <i>{0}</i>", "Remove a Row from the listview.", "RemoveAt");
    
    AddAction(7, af_none, "Clear", "Row", "Clear all Rows", "Remove all Rows from the listview.", "Clear");
    
    AddNumberParam("Index", "The zero-based index of the Row to Change css.");
    AddStringParam("Css", "Css exemple    \"background-color:blue;color:white;\" .");
    AddAction(8, af_none, "Change Row CSS At", "Row", "Change Row <i>{0}</i> CSS to <i>{1}</i>", "Change a Row css.", "ChangeRowCssAt");
    
    AddNumberParam("Row Index", "The zero-based index of the Row to Change the css.");
    AddNumberParam("Cell Index", "The zero-based index of the Cell to Change the css.");
    AddStringParam("Css", "Css exemple    \"background-color:blue;color:white;\" .");
    AddAction(9, af_none, "Change Cell CSS At Row", "Row", "Change Cell <i>{1}</i> At Row <i>{0}</i> CSS to <i>{2}</i>", "Change a Cell css.", "ChangeCellCssAt");
    
    AddNumberParam("Index", "The zero-based index of the Row to Change add a value to.");
    AddStringParam("Key", "Key name where to store the value in the row");
    AddStringParam("Value", "Value to store.");
    AddAction(10, af_none, "Add Value at Row", "Row", "Set <i>{1}</i> = <i>{2}</i> At <i>{0}</i>", "Store a value.", "StoreValueAt");
    
    AddNumberParam("Index", "The zero-based index of the Column to sort by.");
    AddComboParamOption("Ascendant");
    AddComboParamOption("Descendant");
    AddComboParam("Direction", "Select if the sort is Ascendant or Descendant")
    AddAction(11, af_none, "Sort Column", "Sort", "Sort Column of index <i>{0}</i> , Order <i>{1}</i> ", "Sort Column", "sortColumn");
    
    // PlayLive
    AddAction(12, af_none, "Scroll Top", "Listview", "Scroll top", "Scroll to the top line of this object.", "ScrollTop");
    AddAction(13, af_none, "Scroll bottom", "Listview", "Scroll bottom", "Scroll to the bottom of this object.", "ScrollBottom");
    
    AddStringParam("Text", "Set Header (to add \":\" use \\:). Exemple 3 Column       \"Header1:Header2:Header3\"");
    AddAction(14, af_none, "Set Header", "Listview", "Set the header (<i>{0}</i>)", "Set the header.", "SetHeader");
    
    AddNumberParam("Row Index", "The zero-based index of the Row to change the value.");
    AddNumberParam("Cell Index", "The zero-based index of the Cell to change the value.");
    AddStringParam("Value", "Change value (to add \":\" use \\:).");
    AddAction(15, af_none, "Change Value", "Row", "Change value [{0}][{1}] to (<i>{2}</i>)", "Change value at row and cell.", "ChangeValue");
    
    //////////////////////////////////////// 
    // Expressions
    
    // AddExpression(id,			// any positive integer to uniquely identify this expression
    //				 flags,			// (see docs) ef_none, ef_deprecated, ef_return_number, ef_return_string,
    //								// ef_return_any, ef_variadic_parameters (one return flag must be specified)
    //				 list_name,		// currently ignored, but set as if appeared in event wizard
    //				 category,		// category in expressions panel
    //				 exp_name,		// the expression name after the dot, e.g. "foo" for "myobject.foo" - also the runtime function name
    //				 description);	// description in expressions panel
    
    AddExpression(0, ef_return_number, "", "Listview", "RowsCount", "The number of Rows in the listview.");
    AddExpression(1, ef_return_number, "", "Listview", "SelectedRowIndex", "The zero-based index of the currently selected Row.");
    
    AddNumberParam("SubTextIndex", "subText index to get.");
    AddExpression(2, ef_return_string, "", "Listview", "SubTextSelectedRow", "The subText of the currently selected Row.");
    
    AddNumberParam("RowIndex", "Row number to get.");
    AddNumberParam("SubTextIndex", "subText index to get.");
    AddExpression(3, ef_return_string, "", "Listview", "SubTextAt", "The subText of the Nth Row in the listview.");
    
    AddNumberParam("RowIndex", "Row number to get.");
    AddStringParam("Key", "Key name.");
    AddExpression(4, ef_return_string, "", "Listview", "getValueOfKeyAt", "Get stored value of key at a specific row.");
    
    ACESDone();
    
    // Property grid properties for this plugin
    var property_list = [
    	new cr.Property(ept_text,	"Header",				"",			"The initial listiew's header separeted by colon : Exemple header1:header2:header3"),
    	new cr.Property(ept_text,	"Items",				"",			"The initial listiew of items, separated by semicolons ; and colomuns separeted by colon : .Exemple Row1Col1:Row1Col2:Row1Col3;Row2Col1:Row2Col2:Row2Col3;"),
    	new cr.Property(ept_text,	"Colomun Width",	"",			"Set Listview Colomun Width separated by semicolons ;  .Exemple 20%:50%:30%"),
    	new cr.Property(ept_combo,	"Initial visibility",	"Visible",	"Choose whether the list is visible on startup.", "Invisible|Visible"),
    	new cr.Property(ept_text,	"ID (optional)",		"",			"An ID for the control allowing it to be styled with CSS from the page HTML."),
    	new cr.Property(ept_text,	"Header CSS (optional)",		"",		"Add Some CSS to your Listview's header."),
    	new cr.Property(ept_text,	"Rows CSS (optional)",		"",			"Add Some CSS to your Listview's Rows."),
    	new cr.Property(ept_text,	"Cell CSS (optional)",		"",			"Add Some CSS to your Listview's Cells."),
    	new cr.Property(ept_combo,	"Horizontal Alignement",	"Center",		"Set Columns Alignement.", "Left|Center|Right"),
    	new cr.Property(ept_combo,	"Vertical Alignement",	"Center",		"Set Columns Alignement.", "Top|Center|Bottom"),
    	new cr.Property(ept_combo,	"Show Grid",	"No",		"For listview Grid.", "No|Yes"),
    	new cr.Property(ept_combo,	"Show Sorter",	"Yes",		"Show Sorter Icons.", "No|Yes"),
    	new cr.Property(ept_combo,	"Sorter Alignement",	"Right",		"Sorter Alignement .", "Left|Center|Right"),
    	new cr.Property(ept_combo,	"Fixed Header",	"No",		"Header Fixed when scroll.", "No|Yes"),
    	new cr.Property(ept_combo,	"Allow Sort",	"No",		"Allow Sorting.", "No|Yes"),
    	new cr.Property(ept_combo,	"Theme",	"Default",		"Select Listview Theme.", "None|Black-Ice|Blue|Bootstrap|Bootstrap 2|Dark|Default|Dropbox|Green|Grey|Ice|Jui"),
    	new cr.Property(ept_combo,	"Show Y-Scroll",	"Auto",		"Show Y-Scroll.", "Auto|Yes|No")
    ];
    	
    // Called by IDE when a new object type is to be created
    function CreateIDEObjectType()
    {
    	return new IDEObjectType();
    }
    
    // Class representing an object type in the IDE
    function IDEObjectType()
    {
    	assert2(this instanceof arguments.callee, "Constructor called as a function");
    }
    
    // Called by IDE when a new object instance of this type is to be created
    IDEObjectType.prototype.CreateInstance = function(instance)
    {
    	return new IDEInstance(instance);
    }
    
    // Class representing an individual instance of an object in the IDE
    function IDEInstance(instance, type)
    {
    	assert2(this instanceof arguments.callee, "Constructor called as a function");
    	
    	// Save the constructor parameters
    	this.instance = instance;
    	this.type = type;
    	
    	// Set the default property values from the property table
    	this.properties = {};
    	
    	for (var i = 0; i < property_list.length; i++)
    		this.properties[property_list[i].name] = property_list[i].initial_value;
    		
    	// Plugin-specific variables
    	this.just_inserted = false;
    	this.font = null;
    }
    
    IDEInstance.prototype.OnCreate = function()
    {
    	this.instance.SetHotspot(new cr.vector2(0, 0));
    }
    
    IDEInstance.prototype.OnInserted = function()
    {
    	this.instance.SetSize(new cr.vector2(150, 22));
    }
    
    IDEInstance.prototype.OnDoubleClicked = function()
    {
    }
    
    // Called by the IDE after a property has been changed
    IDEInstance.prototype.OnPropertyChanged = function(property_name)
    {
    }
    
    IDEInstance.prototype.OnRendererInit = function(renderer)
    {
    }
    	
    // Called to draw self in the editor
    IDEInstance.prototype.Draw = function(renderer)
    {
    	if (!this.font)
    		this.font = renderer.CreateFont("Arial", 14, false, false);
    		
    	renderer.SetTexture(null);
    	var quad = this.instance.GetBoundingQuad();
    	renderer.Fill(quad, this.properties["Enabled"] === "Yes" ? cr.RGB(255, 255, 255) : cr.RGB(224, 224, 224));
    	renderer.Outline(quad, cr.RGB(0, 0, 0));
    	
    	cr.quad.prototype.offset.call(quad, 4, 2);
    	
    	this.font.DrawText(this.properties["Items"].replace(/;/g, "\n"),
    							quad,
    							cr.RGB(0, 0, 0),
    							ha_left);
    
    }
    
    IDEInstance.prototype.OnRendererReleased = function(renderer)
    {
    	this.font = null;
    }
    [/code:3oll0gef]
    
    [b]runtime[/b]
    [code:3oll0gef]
    // ECMAScript 5 strict mode
    "use strict";
    // Dropbox , Grey , Bootstap Theme dont have Sort icons
    assert2(cr, "cr namespace not created");
    assert2(cr.plugins_, "cr.plugins_ not created");
    
    /////////////////////////////////////
    // Plugin class
    cr.plugins_.Listview = function(runtime)
    {
    	this.runtime = runtime;
    };
    
    (function ()
    {
    	/////////////////////////////////////
    	var pluginProto = cr.plugins_.Listview.prototype;
    		
    	/////////////////////////////////////
    	// Object type class
    	pluginProto.Type = function(plugin)
    	{
    		this.plugin = plugin;
    		this.runtime = plugin.runtime;
    	};
    
    	var typeProto = pluginProto.Type.prototype;
    
    	// called on startup for each object type
    	typeProto.onCreate = function()
    	{
    	};
    
    	/////////////////////////////////////
    	// Instance class
    	pluginProto.Instance = function(type)
    	{
    		this.type = type;
    		this.runtime = type.runtime;
    	};
    	
    	var instanceProto = pluginProto.Instance.prototype;
    
    	/////////////////////////////////////
    	// PlayLive vars
    	var header = [];
    	var lenCol = [];
    	var align = "";
    	var valign = "";
    	
    	// called whenever an instance is created
    	instanceProto.onCreate = function()
    	{
    		// Not supported in DC
    		if (this.runtime.isDomFree)
    		{
    			cr.logexport("[Construct 2] List plugin not supported on this platform - the object will not be created");
    			return;
    		}
    	
    		this.elem = document.createElement("div");
    		this.elem.table = document.createElement("table");
    		this.elem.id = this.properties[4];
    		if($.trim(this.elem.id) <= 0)
    		{
    			this.elem.id = makeid();
    		}
    		this.lastSelection = -1 ;
    
    		$(this.elem).appendTo(this.runtime.canvasdiv ? this.runtime.canvasdiv : "body");
    		
    		// PlayLive
    		if(this.properties[16] == 0)
    			$(this.elem).addClass("scrollable-area-"+this.elem.id).append($(this.elem.table)).css("overflow-y","auto").css("padding","0px").css("margin","0px");
    		else if(this.properties[16] == 1)
    			$(this.elem).addClass("scrollable-area-"+this.elem.id).append($(this.elem.table)).css("overflow-y","scroll").css("padding","0px").css("margin","0px");
    		else if(this.properties[16] == 2)
    			$(this.elem).addClass("scrollable-area-"+this.elem.id).append($(this.elem.table)).css("overflow-y","hidden").css("padding","0px").css("margin","0px");
    		
    		var self = this;
    		var ligne = [];
    		var theme = "";
    		
    		if($.trim(self.properties[0]).length>0)
    		{
    			if(self.properties[0][self.properties[0].length-1] == ":")
    				self.properties[0]= self.properties[0].slice(0,-1);
    			header=self.properties[0].split(":");
    		}
    		
    		if($.trim(self.properties[1]).length>0)
    		{
    			if(self.properties[1][self.properties[1].length-1] == ";")
    				self.properties[1]= self.properties[1].slice(0,-1);
    			ligne=self.properties[1].split(";");
    		}
    		
    		if($.trim(self.properties[2]).length>0)
    		{
    			if(self.properties[2][self.properties[2].length-1] == ":")
    				self.properties[2]= self.properties[2].slice(0,-1);
    			lenCol=self.properties[2].split(":");
    		}
    		
    		switch(self.properties[8])
    		{
    			case 0 : align="left";break;
    			case 1 : align="center";break;
    			case 2 : align="right";break;
    			default : align="center";break;
    		}
    		switch(self.properties[9])
    		{
    			case 0 : valign="top";break;
    			case 1 : valign="center";break;
    			case 2 : valign="bottom";break;
    			default : valign="center";break;
    		}
    		
    		
    		this.align = align;
    		this.valign = valign;
    		//$(self.elem.table).addClass("tablesorter");
    		
    		
    		$(self.elem.table).attr({
    							"border":self.properties[10],
    							"cellpadding":"0px",
    							"cellspacing":"0px",
    							"width":"100%"
    		}).css("color","black");
    
    		if(header.length>0)
    		{
    			$(self.elem.table).append("<thead><tr></tr></thead>");
    			for(var i=0 ; i<header.length ; i++)
    			{
    				$(this.elem.table).find("thead tr").append("<th width='"+lenCol[i]+"' colNum='"+i+"' align='"+align+"' valign='"+valign+"' >"+header[i]+"</th>");
    			}
    		}
    		
    		$(self.elem.table).append("<tbody></tbody>");
    		if(ligne.length>0)
    		{
    			for(var i=0 ; i<ligne.length ; i++)
    			{
    				var rowId = "";
    				do
    				{
    					rowId = makeid();
    				}while( $("#"+rowId).length);
    				var LigneToAdd = $("<tr id-row='"+rowId+"'></tr>");
    				var a = ligne[i].split(":");
    				for(var j=0 ; j<a.length ; j++)
    				{
    					if($.trim(self.properties[7]).length >0)
    						LigneToAdd.append("<td align='"+align+"' valign='"+valign+"' style='"+self.properties[7]+"'>"+a[j]+"</td>");
    					else
    						LigneToAdd.append("<td align='"+align+"' valign='"+valign+"'>"+a[j]+"</td>");
    				}
    				$(self.elem.table).find("tbody").append(LigneToAdd);
    			}
    		}
    
    		if($.trim(self.properties[5]).length >0)
    			$(self.elem.table).find("thead tr").attr("style",self.properties[5]);
    		if($.trim(self.properties[6]).length >0)
    			$(self.elem.table).find("tbody tr").attr("style",self.properties[6]);
    		
    		if( self.properties[14] == 1)
    		{
    			if(self.properties[15] > 0)
    			{
    				switch(self.properties[15])
    				{
    					case 0 : theme="none";break;
    					case 1 : theme="blackice";break;
    					case 2 : theme="blue";break;
    					case 3 : theme="bootstrap";break;
    					case 4 : theme="bootstrap_2";break;
    					case 5 : theme="dark";break;
    					case 6 : theme="default";break;
    					case 7 : theme="dropbox";break;
    					case 8 : theme="green";break;
    					case 9 : theme="grey";break;
    					case 10 : theme="ice";break;
    					case 11 : theme="jui";break;
    					default : theme="default";break;
    				}
    				$(self.elem.table).tablesorter(
    				{
    					theme:theme
    				});
    			}
    			else
    			{
    				$(self.elem.table).tablesorter().removeClass("tablesorter-default");
    			}
    		}
    		else
    		{
    			if(self.properties[15] > 0)
    			{
    				switch(self.properties[15])
    				{
    					case 0 : theme="none";break;
    					case 1 : theme="blackice";break;
    					case 2 : theme="blue";break;
    					case 3 : theme="bootstrap";break;
    					case 4 : theme="bootstrap_2";break;
    					case 5 : theme="dark";break;
    					case 6 : theme="default";break;
    					case 7 : theme="dropbox";break;
    					case 8 : theme="green";break;
    					case 9 : theme="grey";break;
    					case 10 : theme="ice";break;
    					case 11 : theme="jui";break;
    					default : theme="default";break;
    				}
    				$(self.elem.table).addClass("tablesorter-"+theme);
    			}
    		}
    
    		// PlayLive
    		if( self.properties[13] == 1)
    			$(self.elem.table).stickyTableHeaders({ scrollableArea: $(".scrollable-area-"+this.elem.id) })
    
    		var sorterAlign = 2;
    		var sorterVisib = "initial";
    
    		switch(self.properties[12])
    		{
    			case 0 : sorterAlign="left";break;
    			case 1 : sorterAlign="center";break;
    			case 2 : sorterAlign="right";break;
    			default : sorterVisib="right";break;
    		}
    
    		$(self.elem.table).find("thead tr .header").css(
    		{
    			"background-position":"center "+sorterAlign
    		});
    		if( self.properties[11] == 0)
    		{
    			$(self.elem.table).find("thead tr .header").css(
    			{
    				"background-image":"none"
    			});
    		}
    		
    		
    		$(this.elem.table).find("tbody").on('click' , 'tr', function() 
    		{
    			if( parseInt($(this).index()) != self.lastSelection)
    			{
    				self.lastSelection = parseInt($(this).index());
    				self.runtime.trigger(cr.plugins_.Listview.prototype.cnds.OnSelectionChanged, self);
    			}
    		});
    		
    		$(this.elem.table).find("tbody").on('mouseover' , 'tr', function() 
    		{
    			self.runtime.trigger(cr.plugins_.Listview.prototype.cnds.OnHover, self);
    		});
    		
    		this.elem.onclick = function(e) {
    				e.stopPropagation();
    				self.runtime.isInUserInputEvent = true;
    				self.runtime.trigger(cr.plugins_.Listview.prototype.cnds.OnClicked, self);
    				self.runtime.isInUserInputEvent = false;
    			};
    		
    		this.elem.ondblclick = function(e) {
    				e.stopPropagation();
    				self.runtime.isInUserInputEvent = true;
    				self.runtime.trigger(cr.plugins_.Listview.prototype.cnds.OnDoubleClicked, self);
    				self.runtime.isInUserInputEvent = false;
    			};
    		
    		// Prevent touches reaching the canvas
    		this.elem.addEventListener("touchstart", function (e) {
    			e.stopPropagation();
    		}, false);
    		
    		this.elem.addEventListener("touchmove", function (e) {
    			e.stopPropagation();
    		}, false);
    		
    		this.elem.addEventListener("touchend", function (e) {
    			e.stopPropagation();
    		}, false);
    		
    		// Prevent clicks being blocked
    		jQuery(this.elem).mousedown(function (e) {
    			e.stopPropagation();
    		});
    		
    		jQuery(this.elem).mouseup(function (e) {
    			e.stopPropagation();
    		});
    		
    		this.lastLeft = 0;
    		this.lastTop = 0;
    		this.lastRight = 0;
    		this.lastBottom = 0;
    		this.lastWinWidth = 0;
    		this.lastWinHeight = 0;
    		this.isVisible = true;
    		
    		this.updatePosition(true);
    		
    		this.runtime.tickMe(this);
    	};
    	
    	instanceProto.saveToJSON = function ()
    	{
    		var o = {
    			"tooltip": this.elem.title,
    			"disabled": !!this.elem.disabled,
    			"items": [],
    			"sel": []
    		};
    		
    		var i, len;
    		var itemsarr = o["items"];
    		
    		for (i = 0, len = this.elem.length; i < len; i++)
    		{
    			itemsarr.push(this.elem.options[i].text);
    		}
    		
    		var selarr = o["sel"];
    		
    		if (this.elem["multiple"])
    		{
    			for (i = 0, len = this.elem.length; i < len; i++)
    			{
    				if (this.elem.options[i].selected)
    					selarr.push(i);
    			}
    		}
    		else
    		{
    			selarr.push(this.elem["selectedIndex"]);
    		}
    		
    		return o;
    	};
    	
    	instanceProto.loadFromJSON = function (o)
    	{
    		this.elem.title = o["tooltip"];
    		this.elem.disabled = o["disabled"];
    		
    		var itemsarr = o["items"];
    		
    		// Clear the list
    		while (this.elem.length)
    			this.elem.remove(this.elem.length - 1);
    			
    		var i, len, opt;
    		for (i = 0, len = itemsarr.length; i < len; i++)
    		{
    			opt = document.createElement("option");
    			opt.text = itemsarr[i];
    			this.elem.add(opt);
    		}
    		
    		var selarr = o["sel"];
    		
    		if (this.elem["multiple"])
    		{
    			for (i = 0, len = selarr.length; i < len; i++)
    			{
    				if (selarr[i] < this.elem.length)
    					this.elem.options[selarr[i]].selected = true;
    			}
    		}
    		else if (selarr.length >= 1)
    		{
    			this.elem["selectedIndex"] = selarr[0];
    		}
    	};
    	
    	instanceProto.onDestroy = function ()
    	{
    		if (this.runtime.isDomFree)
    				return;
    		
    		jQuery(this.elem).remove();
    		this.elem = null;
    	};
    	
    	instanceProto.tick = function ()
    	{
    		this.updatePosition();
    	};
    	
    	instanceProto.updatePosition = function (first)
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		var left = this.layer.layerToCanvas(this.x, this.y, true);
    		var top = this.layer.layerToCanvas(this.x, this.y, false);
    		var right = this.layer.layerToCanvas(this.x + this.width, this.y + this.height, true);
    		var bottom = this.layer.layerToCanvas(this.x + this.width, this.y + this.height, false);
    		
    		var rightEdge = this.runtime.width / this.runtime.devicePixelRatio;
    		var bottomEdge = this.runtime.height / this.runtime.devicePixelRatio;
    		
    		// Is entirely offscreen or invisible: hide
    		if (!this.visible || !this.layer.visible || right <= 0 || bottom <= 0 || left >= rightEdge || top >= bottomEdge)
    		{
    			if (this.isVisible)
    				jQuery(this.elem).hide();
    			
    			this.isVisible = false;
    			return;
    		}
    		
    		// Truncate to canvas size
    		if (left < 1)
    			left = 1;
    		if (top < 1)
    			top = 1;
    		if (right >= rightEdge)
    			right = rightEdge - 1;
    		if (bottom >= bottomEdge)
    			bottom = bottomEdge - 1;
    		
    		var curWinWidth = window.innerWidth;
    		var curWinHeight = window.innerHeight;
    			
    		// Avoid redundant updates
    		if (!first && this.lastLeft === left && this.lastTop === top && this.lastRight === right && this.lastBottom === bottom && this.lastWinWidth === curWinWidth && this.lastWinHeight === curWinHeight)
    		{
    			if (!this.isVisible)
    			{
    				jQuery(this.elem).show();
    				this.isVisible = true;
    			}
    			
    			return;
    		}
    			
    		this.lastLeft = left;
    		this.lastTop = top;
    		this.lastRight = right;
    		this.lastBottom = bottom;
    		this.lastWinWidth = curWinWidth;
    		this.lastWinHeight = curWinHeight;
    		
    		if (!this.isVisible)
    		{
    			jQuery(this.elem).show();
    			this.isVisible = true;
    		}
    		
    		var offx = Math.round(left) + jQuery(this.runtime.canvas).offset().left;
    		var offy = Math.round(top) + jQuery(this.runtime.canvas).offset().top;
    		jQuery(this.elem).css("position", "absolute");
    		jQuery(this.elem).offset({left: offx, top: offy});
    		jQuery(this.elem).width(Math.round(right - left));
    		jQuery(this.elem).height(Math.round(bottom - top));
    		
    	};
    	
    	// only called if a layout object
    	instanceProto.draw = function(ctx)
    	{
    	};
    	
    	instanceProto.drawGL = function(glw)
    	{
    	};
    	
    	/**BEGIN-PREVIEWONLY**/
    	instanceProto.getDebuggerValues = function (propsections)
    	{
    		propsections.push({
    			"title": "List",
    			"properties": [
    				{"name": "Item count", "value": this.elem.length, "readonly": true},
    				{"name": "Enabled", "value": !this.elem.disabled},
    				{"name": "Tooltip", "value": this.elem.title},
    				{"name": "Selected index", "value": this.elem.selectedIndex}
    			]
    		});
    		
    		var props = [], i, len;
    		for (i = 0, len = this.elem.length; i < len; ++i)
    		{
    			props.push({"name": i.toString(), "value": this.elem.options[i].text});
    		}
    		
    		propsections.push({
    			"title": "Items",
    			"properties": props
    		});
    	};
    	
    	instanceProto.onDebugValueEdited = function (header, name, value)
    	{
    		if (header === "List")
    		{
    			switch (name) {
    			case "Enabled":
    				this.elem.disabled = !value;
    				break;
    			case "Tooltip":
    				this.elem.title = value;
    				break;
    			case "Selected index":
    				this.elem.selectedIndex = value;
    				break;
    			}
    		}
    		else if (header === "Items")
    		{
    			this.elem.options[parseInt(name, 10)].text = value;
    		}
    	};
    	/**END-PREVIEWONLY**/
    
    	//////////////////////////////////////
    	// Conditions
    	function Cnds() {};
    	
    	Cnds.prototype.OnSelectionChanged = function ()
    	{
    		return true;
    	};
    	
    	Cnds.prototype.OnClicked = function ()
    	{
    		return true;
    	};
    	
    	Cnds.prototype.OnDoubleClicked = function ()
    	{
    		return true;
    	};
    	
    	Cnds.prototype.OnHover = function ()
    	{
    		return true;
    	};
    	
    	
    	pluginProto.cnds = new Cnds();
    	
    	//////////////////////////////////////
    	// Actions
    	function Acts() {};
    	
    	Acts.prototype.SelectedRow = function (i)
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		this.lastSelection = i;
    		$(this.elem.table).find("tbody tr.selected").removeClass("selected");
    		$(this.elem.table).find("tbody tr").eq(i).addClass("selected");
    	};
    	
    	Acts.prototype.SetVisible = function (vis)
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		this.visible = (vis !== 0);
    	};
    	
    	Acts.prototype.SetFocus = function ()
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		this.elem.table.focus();
    	};
    	
    	Acts.prototype.SetBlur = function ()
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		this.elem.table.blur();
    	};
    
    	
    	Acts.prototype.AddRow = function (text_)
    	{
    		if (this.runtime.isDomFree)
    			return;
    
    		var rowId = "";
    		do { rowId = makeid(); }
    		while( $("#"+rowId).length);
    		
    		
    		var LigneToAdd = $("<tr id-row='"+rowId+"'></tr>");
    		var a = text_.split(":");
    		var r = a.toString().replace(/\\,/g, ":");
    		a = r.split(",");
    
    		for(var j=0 ; j<a.length ; j++)
    		{
    			LigneToAdd.append("<td align='"+this.align+"' valign='"+this.valign+"' style='"+this.properties[7]+"'>"+a[j]+"</td>");
    		}
    
    		$(this.elem.table).find("tbody").append(LigneToAdd);
    
    		if( this.properties[14] == 1)
    		{
    			$(this.elem.table).trigger('update');     
    			$(this.elem.table).trigger("sorton",[$(this.elem.table).get(0).config.sortList]); 
    		}
    	};
    	
    	Acts.prototype.AddRowAt = function (index_, text_)
    	{
    		if (this.runtime.isDomFree)
    			return;
    
    		var rowId = "";
    		do
    		{
    			rowId = makeid();
    		}while( $("#"+rowId).length);
    		
    		
    		var LigneToAdd = $("<tr id-row='"+rowId+"'></tr>");
    		var a = text_.split(":");
    		var r = a.toString().replace(/,,/g, ":");
    		a = r.split(",");
    		for(var j=0 ; j<a.length ; j++)
    		{
    			LigneToAdd.append("<td align='"+this.align+"' valign='"+this.valign+"' style='"+this.properties[7]+"'>"+a[j]+"</td>");
    		}
    		
    		if(index_ > 0)
    			LigneToAdd.insertBefore($(this.elem.table).find("tbody tr").eq(index_));
    		else
    			LigneToAdd.appendTo($(this.elem.table).find("tbody"));
    		
    		if( this.properties[14] == 1 )
    		{
    			$(this.elem.table).trigger('update');
    			$(this.elem.table).trigger("sorton",[$(this.elem.table).get(0).config.sortList]); 
    		}
    	};
    	
    	Acts.prototype.RemoveAt = function (index_)
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		if(index_ == this.lastSelection)
    				this.lastSelection = -1 ;
    
    		$(this.elem.table).find("tbody tr").eq(index_).remove();
    	};
    
    	Acts.prototype.ChangeRowCssAt = function (index_,css)
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		var item = $(this.elem.table).find("tbody tr").eq(index_);
    		if(item.is("[style]"))
    			item.attr("style",item.attr("style")+css);
    		else
    			item.attr("style",css);
    	};
    	
    	Acts.prototype.ChangeCellCssAt = function (index_,index_2,css)
    	{
    		if (this.runtime.isDomFree)
    			return;
    
    		var item = $(this.elem.table).find("tbody tr").eq(index_).find("td").eq(index_2);
    		if(item.is("[style]"))
    			item.attr("style",item.attr("style")+css);
    		else
    			item.attr("style",css);
    	};
    	
    	Acts.prototype.StoreValueAt = function (index_,key,val)
    	{
    		if (this.runtime.isDomFree)
    			return;
    
    		$(this.elem.table).find("tbody tr").eq(index_).attr(key,val);
    	};
    	
    	Acts.prototype.sortColumn = function (index_,val)
    	{
    		if (this.runtime.isDomFree)
    			return;
    		$("table").trigger("sorton",[ [[index_,val]] ]);
    		
    	};
    	
    	Acts.prototype.Clear = function ()
    	{
    		$(this.elem.table).find("tbody tr").remove();
    	};
    
    	Acts.prototype.ScrollTop = function () // PlayLive
    	{
    		if (this.runtime.isDomFree)
    			return;
    
            this.elem.scrollTop = 0;
    	};
    
    	Acts.prototype.ScrollBottom = function () // PlayLive
    	{
    		if (this.runtime.isDomFree)
    			return;
    		
    		this.elem.scrollTop = this.elem.scrollHeight;
    	};
    
    	Acts.prototype.SetHeader = function (str_) // PlayLive
    	{
    		if (this.runtime.isDomFree)
    			return;
    /*
    		var newheader = str_.split(":");
    
    		if (newheader.length > 0)
    		{
    			$(this.elem.table).find("thead tr th").text("");
    			var table = $(this.elem.table).find("thead tr th").length / 2;
    
    			while (newheader.length > table) {
    				$(this.elem.table).find("thead tr").append("<th align='"+align+"' valign='"+valign+"'></th>");
    				table++;
    			}
    
    			while (newheader.length < table) {
    				$(this.elem.table).find("thead tr th:eq("+(table)+")").remove();
    				$(this.elem.table).find("thead tr th:eq("+(table)+")").remove();
    				table--;
    			}
    
    			for (var i = 0; i < table; i++) {
    				$(this.elem.table).find("thead tr th:eq("+i+")").text(newheader[i]);
    			}
    		}
    */
    		var newheader = str_.split(":");
    		$(this.elem.table).find("thead tr th").text("");
    
    		for (var i = 0; i < newheader.length; i++)
    			$(this.elem.table).find("thead tr th:eq("+i+")").text(newheader[i]);
    
    	};
    
    	Acts.prototype.ChangeValue = function (row_, cell_, str_) // PlayLive
    	{
    		$(this.elem.table).find("tbody tr:eq("+row_+") td:eq("+cell_+")").text(str_);
    	};
    	
    	pluginProto.acts = new Acts();
    	
    	//////////////////////////////////////
    	// Expressions
    	function Exps() {};
    	
    	Exps.prototype.RowsCount = function (ret)
    	{
    		if (this.runtime.isDomFree)
    		{
    			ret.set_int(0);
    			return;
    		}
    		
    		ret.set_int($(this.elem.table).find("tbody tr").length);
    	};
    	
    	Exps.prototype.SelectedRowIndex = function (ret)
    	{
    		if (this.runtime.isDomFree)
    		{
    			ret.set_int(0);
    			return;
    		}
    		ret.set_int(this.lastSelection);
    	};
    	
    	Exps.prototype.SubTextSelectedRow = function (ret,SubTextIndex)
    	{
    		if (this.runtime.isDomFree)
    		{
    			ret.set_string("");
    			return;
    		}
    		
    		ret.set_string($(this.elem.table).find("tbody tr").eq(this.lastSelection).find("td").eq(SubTextIndex).text());
    	};
    	
    	
    	Exps.prototype.SubTextAt = function (ret,LigneIndex , SubTextIndex)
    	{
    		if (this.runtime.isDomFree)
    		{
    			ret.set_string("");
    			return;
    		}
    		
    		ret.set_string($(this.elem.table).find("tbody tr").eq(LigneIndex).find("td").eq(SubTextIndex).text() );
    	};
    	
    	
    	Exps.prototype.getValueOfKeyAt = function (ret,RowIndex , Key) // PlayLive
    	{
    		if (this.runtime.isDomFree)
    		{
    			ret.set_string("");
    			return;
    		}
    		
    		ret.set_string($(this.elem.table).find("tbody tr").eq(RowIndex).attr(Key) );
    	};
    	pluginProto.exps = new Exps();
    
    }());
    
    function makeid()
    {
        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    	
        for( var i=0; i < 10; i++ )
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        return text;
    }
    
    function createCSSSelector(selector, style) 
    {
    	var cssrules =  $("<style type='text/css'> </style>").appendTo("head");
    	cssrules.append(selector+"{ "+style+" }"); 
    }
    [/code:3oll0gef]
    
    
    Thanks for the update!
    
    Does someone can tell me how to fill the listview with a JSON as source? oO Somehow I cant manage that...