Commit c921bb14 authored by Ronny's avatar Ronny Committed by techird

1. protocal添加mindmanager xmind freemind解析

2. 添加lib zip、inflate、xml2json,同时修改import.php和index.html
3. social.js添加格式解析
4. dropfile.js添加格式解析
5. minder.data.js将import改成支持同步和异步导入
6. saveto.js 如果没有encode则不添加到dropmenu栏
parent 9c259ca5
...@@ -65,6 +65,9 @@ $dependency = Array( ...@@ -65,6 +65,9 @@ $dependency = Array(
,'src/adapter/node.js' ,'src/adapter/node.js'
,'src/adapter/contextmenu.js' ,'src/adapter/contextmenu.js'
,'src/adapter/dialog.js' ,'src/adapter/dialog.js'
,'src/protocal/xmind.js'
,'src/protocal/freemind.js'
,'src/protocal/mindmanager.js'
,'src/protocal/plain.js' ,'src/protocal/plain.js'
,'src/protocal/json.js' ,'src/protocal/json.js'
,'src/protocal/png.js' ,'src/protocal/png.js'
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
<script src="../kityminder.config.js" charset="utf-8"></script> <script src="../kityminder.config.js" charset="utf-8"></script>
<script src="../lang/zh-cn/zh-cn.js" charset="utf-8"></script> <script src="../lang/zh-cn/zh-cn.js" charset="utf-8"></script>
<script src="../lib/zip.js" charset="utf-8"></script>
<script src="../lib/jquery.xml2json.js" charset="utf-8"></script>
<script src="../lib/baidu-frontia-js-full-1.0.0.js" charset="utf-8"></script> <script src="../lib/baidu-frontia-js-full-1.0.0.js" charset="utf-8"></script>
<script src="../social/draftmanager.js" charset="utf-8"></script> <script src="../social/draftmanager.js" charset="utf-8"></script>
<script src="../social/social.js" charset="utf-8"></script> <script src="../social/social.js" charset="utf-8"></script>
......
This diff is collapsed.
/*
### jQuery XML to JSON Plugin v1.3 - 2013-02-18 ###
* http://www.fyneworks.com/ - diego@fyneworks.com
* Licensed under http://en.wikipedia.org/wiki/MIT_License
###
Website: http://www.fyneworks.com/jquery/xml-to-json/
*//*
# INSPIRED BY: http://www.terracoder.com/
AND: http://www.thomasfrank.se/xml_to_json.html
AND: http://www.kawa.net/works/js/xml/objtree-e.html
*//*
This simple script converts XML (document of code) into a JSON object. It is the combination of 2
'xml to json' great parsers (see below) which allows for both 'simple' and 'extended' parsing modes.
*/
// Avoid collisions
;if(window.jQuery) (function($){
// Add function to jQuery namespace
$.extend({
// converts xml documents and xml text to json object
xml2json: function(xml, extended) {
if(!xml) return {}; // quick fail
//### PARSER LIBRARY
// Core function
function parseXML(node, simple){
if(!node) return null;
var txt = '', obj = null, att = null;
var nt = node.nodeType, nn = jsVar(node.localName || node.nodeName);
var nv = node.text || node.nodeValue || '';
/*DBG*/ //if(window.console) console.log(['x2j',nn,nt,nv.length+' bytes']);
if(node.childNodes){
if(node.childNodes.length>0){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'CHILDREN',node.childNodes]);
$.each(node.childNodes, function(n,cn){
var cnt = cn.nodeType, cnn = jsVar(cn.localName || cn.nodeName);
var cnv = cn.text || cn.nodeValue || '';
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>a',cnn,cnt,cnv]);
if(cnt == 8){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>b',cnn,'COMMENT (ignore)']);
return; // ignore comment node
}
else if(cnt == 3 || cnt == 4 || !cnn){
// ignore white-space in between tags
if(cnv.match(/^\s+$/)){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>c',cnn,'WHITE-SPACE (ignore)']);
return;
};
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>d',cnn,'TEXT']);
txt += cnv.replace(/^\s+/,'').replace(/\s+$/,'');
// make sure we ditch trailing spaces from markup
}
else{
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>e',cnn,'OBJECT']);
obj = obj || {};
if(obj[cnn]){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>f',cnn,'ARRAY']);
// http://forum.jquery.com/topic/jquery-jquery-xml2json-problems-when-siblings-of-the-same-tagname-only-have-a-textnode-as-a-child
if(!obj[cnn].length) obj[cnn] = myArr(obj[cnn]);
obj[cnn] = myArr(obj[cnn]);
obj[cnn][ obj[cnn].length ] = parseXML(cn, true/* simple */);
obj[cnn].length = obj[cnn].length;
}
else{
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>g',cnn,'dig deeper...']);
obj[cnn] = parseXML(cn);
};
};
});
};//node.childNodes.length>0
};//node.childNodes
if(node.attributes){
if(node.attributes.length>0){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'ATTRIBUTES',node.attributes])
att = {}; obj = obj || {};
$.each(node.attributes, function(a,at){
var atn = jsVar(at.name), atv = at.value;
att[atn] = atv;
if(obj[atn]){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'attr>',atn,'ARRAY']);
// http://forum.jquery.com/topic/jquery-jquery-xml2json-problems-when-siblings-of-the-same-tagname-only-have-a-textnode-as-a-child
//if(!obj[atn].length) obj[atn] = myArr(obj[atn]);//[ obj[ atn ] ];
obj[cnn] = myArr(obj[cnn]);
obj[atn][ obj[atn].length ] = atv;
obj[atn].length = obj[atn].length;
}
else{
/*DBG*/ //if(window.console) console.log(['x2j',nn,'attr>',atn,'TEXT']);
obj[atn] = atv;
};
});
//obj['attributes'] = att;
};//node.attributes.length>0
};//node.attributes
if(obj){
obj = $.extend( (txt!='' ? new String(txt) : {}),/* {text:txt},*/ obj || {}/*, att || {}*/);
//txt = (obj.text) ? (typeof(obj.text)=='object' ? obj.text : [obj.text || '']).concat([txt]) : txt;
txt = (obj.text) ? ([obj.text || '']).concat([txt]) : txt;
if(txt) obj.text = txt;
txt = '';
};
var out = obj || txt;
//console.log([extended, simple, out]);
if(extended){
if(txt) out = {};//new String(out);
txt = out.text || txt || '';
if(txt) out.text = txt;
if(!simple) out = myArr(out);
};
return out;
};// parseXML
// Core Function End
// Utility functions
var jsVar = function(s){ return String(s || '').replace(/-/g,"_"); };
// NEW isNum function: 01/09/2010
// Thanks to Emile Grau, GigaTecnologies S.L., www.gigatransfer.com, www.mygigamail.com
function isNum(s){
// based on utility function isNum from xml2json plugin (http://www.fyneworks.com/ - diego@fyneworks.com)
// few bugs corrected from original function :
// - syntax error : regexp.test(string) instead of string.test(reg)
// - regexp modified to accept comma as decimal mark (latin syntax : 25,24 )
// - regexp modified to reject if no number before decimal mark : ".7" is not accepted
// - string is "trimmed", allowing to accept space at the beginning and end of string
var regexp=/^((-)?([0-9]+)(([\.\,]{0,1})([0-9]+))?$)/
return (typeof s == "number") || regexp.test(String((s && typeof s == "string") ? jQuery.trim(s) : ''));
};
// OLD isNum function: (for reference only)
//var isNum = function(s){ return (typeof s == "number") || String((s && typeof s == "string") ? s : '').test(/^((-)?([0-9]*)((\.{0,1})([0-9]+))?$)/); };
var myArr = function(o){
// http://forum.jquery.com/topic/jquery-jquery-xml2json-problems-when-siblings-of-the-same-tagname-only-have-a-textnode-as-a-child
//if(!o.length) o = [ o ]; o.length=o.length;
if(!$.isArray(o)) o = [ o ]; o.length=o.length;
// here is where you can attach additional functionality, such as searching and sorting...
return o;
};
// Utility functions End
//### PARSER LIBRARY END
// Convert plain text to xml
if(typeof xml=='string') xml = $.text2xml(xml);
// Quick fail if not xml (or if this is a node)
if(!xml.nodeType) return;
if(xml.nodeType == 3 || xml.nodeType == 4) return xml.nodeValue;
// Find xml root node
var root = (xml.nodeType == 9) ? xml.documentElement : xml;
// Convert xml to json
var out = parseXML(root, true /* simple */);
// Clean-up memory
xml = null; root = null;
// Send output
return out;
},
// Convert text to XML DOM
text2xml: function(str) {
// NOTE: I'd like to use jQuery for this, but jQuery makes all tags uppercase
//return $(xml)[0];
/* prior to jquery 1.9 */
/*
var out;
try{
var xml = ((!$.support.opacity && !$.support.style))?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
xml.async = false;
}catch(e){ throw new Error("XML Parser could not be instantiated") };
try{
if((!$.support.opacity && !$.support.style)) out = (xml.loadXML(str))?xml:false;
else out = xml.parseFromString(str, "text/xml");
}catch(e){ throw new Error("Error parsing XML string") };
return out;
*/
/* jquery 1.9+ */
return $.parseXML(str);
}
}); // extend $
})(jQuery);
\ No newline at end of file
This diff is collapsed.
...@@ -74,9 +74,8 @@ $( function () { ...@@ -74,9 +74,8 @@ $( function () {
// 脑图实例 // 脑图实例
minder = window.km, minder = window.km,
// 草稿箱 // 草稿箱
draftManager = new window.DraftManager( minder ), draftManager = window.draftManager || new window.DraftManager( minder ),
// 当前是否要检测文档内容是否变化的开关 // 当前是否要检测文档内容是否变化的开关
watchingChanges = true, watchingChanges = true,
...@@ -316,31 +315,92 @@ $( function () { ...@@ -316,31 +315,92 @@ $( function () {
sto.getFileUrl( remotePath, { sto.getFileUrl( remotePath, {
success: function ( url ) { success: function ( url ) {
// the url to download the file on cloud dist
var arr = remotePath.split( '.' );
var format = arr[ arr.length - 1 ];
if ( format in loadFile ) {
loadFile[ format ]( url );
}
},
error: notice
} );
}
var loadFile = {
'km': loadPlainType,
'json': loadPlainType,
'xmind': loadXMind,
'mmap': loadMindManager,
'mm': loadFreeMind
};
function loadPlainType( url ) {
$.ajax( { $.ajax( {
cache: false, cache: false,
url: url, url: url,
dataType: 'text', dataType: 'text',
success: function ( result ) { success: function ( result ) {
importFile( result, 'json' );
}
} );
}
function loadXMind( url ) {
var xhr = new XMLHttpRequest();
xhr.open( "get", url, true );
xhr.responseType = "blob";
xhr.onload = function () {
if ( this.status == 200 && this.readyState ) {
var blob = this.response;
importFile( blob, 'xmind' );
}
};
xhr.send();
}
function loadMindManager( url ) {
var xhr = new XMLHttpRequest();
xhr.open( "get", url, true );
xhr.responseType = "blob";
xhr.onload = function () {
if ( this.status == 200 && this.readyState ) {
var blob = this.response;
importFile( blob, 'mindmanager' );
}
};
xhr.send();
}
function loadFreeMind( url ) {
$.ajax( {
cache: false,
url: url,
dataType: 'text',
success: function ( result ) {
importFile( result, 'freemind' );
}
} );
}
// 见文件数据导入minder
function importFile( data, format ) {
watchingChanges = false; watchingChanges = false;
minder.importData( result, 'json' ); minder.importData( data, format );
if ( !draftManager.openByPath( remotePath ) ) { if ( !draftManager.openByPath( remotePath ) ) {
draftManager.create(); draftManager.create();
} }
draftManager.save( remotePath ); draftManager.save( remotePath );
draftManager.sync(); draftManager.sync();
minder.execCommand( 'camera', minder.getRoot() ); minder.execCommand( 'camera', minder.getRoot() );
$user_btn.loading( false ).text( getFileName( remotePath ) ); $user_btn.loading( false ).text( getFileName( remotePath ) );
watchingChanges = true; watchingChanges = true;
} }
} );
},
error: notice
} );
}
// 添加文件到最近文件列表 // 添加文件到最近文件列表
function addToRecentMenu( list ) { function addToRecentMenu( list ) {
......
...@@ -17,10 +17,12 @@ KM.registerToolbarUI( 'saveto', function ( name ) { ...@@ -17,10 +17,12 @@ KM.registerToolbarUI( 'saveto', function ( name ) {
utils.each( KityMinder.getAllRegisteredProtocals(), function ( k ) { utils.each( KityMinder.getAllRegisteredProtocals(), function ( k ) {
var p = KityMinder.findProtocal( k ); var p = KityMinder.findProtocal( k );
if( p.encode ){
var text = p.fileDescription + '(' + p.fileExtension + ')'; var text = p.fileDescription + '(' + p.fileExtension + ')';
options.value.push( k ); options.value.push( k );
options.items.push( text ); options.items.push( text );
options.autowidthitem.push( $.wordCountAdaptive( text ), true ); options.autowidthitem.push( $.wordCountAdaptive( text ), true );
}
} ); } );
......
...@@ -97,6 +97,18 @@ kity.extendClass( Minder, { ...@@ -97,6 +97,18 @@ kity.extendClass( Minder, {
json = params.json || ( params.json = protocal.decode( local ) ); json = params.json || ( params.json = protocal.decode( local ) );
if ( typeof json === 'object' && 'then' in json ) {
var self = this;
json.then( local, function ( data ) {
self._afterImportData( data, params );
} );
} else {
this._afterImportData( json, params );
}
return this;
},
_afterImportData: function ( json, params ) {
this._fire( new MinderEvent( 'preimport', params, false ) ); this._fire( new MinderEvent( 'preimport', params, false ) );
// 删除当前所有节点 // 删除当前所有节点
...@@ -115,7 +127,6 @@ kity.extendClass( Minder, { ...@@ -115,7 +127,6 @@ kity.extendClass( Minder, {
this._firePharse( { this._firePharse( {
type: 'interactchange' type: 'interactchange'
} ); } );
return this;
} }
} ); } );
\ No newline at end of file
...@@ -16,13 +16,50 @@ KityMinder.registerModule( "DropFile", function () { ...@@ -16,13 +16,50 @@ KityMinder.registerModule( "DropFile", function () {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
var minder = this; var minder = this;
if ( e.dataTransfer.files ) {
var files = e.dataTransfer.files;
if ( files ) {
var file = files[0];
var ext = file.type || (/(.)\w+$/).exec(file.name)[0];
if( (/xmind/g).test(ext) ){ //xmind zip
importSync( minder, file, 'xmind' );
}else if( (/mmap/g).test(ext) ){ // mindmanager zip
importSync( minder, file, 'mindmanager' );
}else if( (/mm/g).test(ext) ){ //freemind xml
importAsync( minder, file, 'freemind' );
}else{// txt json
importAsync( minder, file );
}
}
}
// 同步加载文件
function importSync( minder, file, protocal ){
minder.importData( file, protocal );//zip文件的import是同步的
manageDraft( minder );
minder.execCommand( 'camera', minder.getRoot() );
}
// 异步加载文件
function importAsync( minder, file, protocal ){
var reader = new FileReader(); var reader = new FileReader();
reader.onload = function ( e ) { reader.onload = function ( e ) {
minder.importData( e.target.result ); minder.importData( e.target.result, protocal );//纯文本文件的import是同步的
manageDraft( minder );
minder.execCommand( 'camera', minder.getRoot() );
}; };
reader.readAsText( e.dataTransfer.files[ 0 ] ); reader.readAsText( file );
}
function manageDraft( minder ){
if( !window.draftManager ){
window.draftManager = new window.DraftManager( minder );
} }
window.draftManager.create();
} }
return { return {
......
/*
http://www.xmind.net/developer/
Parsing XMind file
XMind files are generated in XMind Workbook (.xmind) format, an open format that is based on the principles of OpenDocument. It consists of a ZIP compressed archive containing separate XML documents for content and styles, a .jpg image file for thumbnails, and directories for related attachments.
*/
KityMinder.registerProtocal( 'freemind', function () {
var markerMap = {
'full-1' : ['PriorityIcon', 1]
,'full-2' : ['PriorityIcon', 2]
,'full-3' : ['PriorityIcon', 3]
,'full-4' : ['PriorityIcon', 4]
,'full-5' : ['PriorityIcon', 5]
,'full-6' : null
,'full-7' : null
,'full-8' : null
,'full-9' : null
,'full-0' : null
};
function processTopic(topic, obj){
//处理文本
obj.data = { text : topic.TEXT };
// 处理标签
if(topic.icon){
var icons = topic.icon;
if(icons.length && icons.length > 0){
for (var i in icons) {
var type = markerMap[ icons[i]['BUILTIN'] ];
type && (obj.data[ type[0] ] = type[1]);
}
}else{
var type = markerMap[ icons['BUILTIN'] ];
type && (obj.data[ type[0] ] = type[1]);
}
}
//处理子节点
if( topic.node ){
var tmp = topic.node;
if( tmp.length && tmp.length > 0 ){ //多个子节点
obj.children = [];
for(var i in tmp){
obj.children.push({});
processTopic(tmp[i], obj.children[i]);
}
}else{ //一个子节点
obj.children = [{}];
processTopic(tmp, obj.children[0]);
}
}
};
function xml2km(xml){
var json = $.xml2json(xml);
var result = {};
processTopic(json.node, result);
return result;
};
return {
fileDescription: 'xmind格式文件',
fileExtension: '.xmind',
decode: function ( local ) {
var json = xml2km( local );
return json;
},
// recognize: recognize,
recognizePriority: -1
};
} );
/*
http://www.xmind.net/developer/
Parsing XMind file
XMind files are generated in XMind Workbook (.xmind) format, an open format that is based on the principles of OpenDocument. It consists of a ZIP compressed archive containing separate XML documents for content and styles, a .jpg image file for thumbnails, and directories for related attachments.
*/
KityMinder.registerProtocal( 'mindmanager', function () {
var markerMap = {
'urn:mindjet:Prio1' : ['PriorityIcon', 1]
,'urn:mindjet:Prio2' : ['PriorityIcon', 2]
,'urn:mindjet:Prio3' : ['PriorityIcon', 3]
,'urn:mindjet:Prio4' : ['PriorityIcon', 4]
,'urn:mindjet:Prio5' : ['PriorityIcon', 5]
,'0' : ['ProgressIcon', 1]
,'25' : ['ProgressIcon', 2]
,'50' : ['ProgressIcon', 3]
,'75' : ['ProgressIcon', 4]
,'100' : ['ProgressIcon', 5]
};
function processTopic(topic, obj){
//处理文本
obj.data = { text : topic.Text && topic.Text.PlainText || '_' }; // 节点默认的文本,没有Text属性
// 处理标签
if(topic.Task){
var type;
if(topic.Task.TaskPriority){
type = markerMap[ topic.Task.TaskPriority ];
type && (obj.data[ type[0] ] = type[1]);
}
if(topic.Task.TaskPercentage){
type = markerMap[ topic.Task.TaskPercentage ];
type && (obj.data[ type[0] ] = type[1]);
}
}
//处理子节点
if( topic.SubTopics && topic.SubTopics.Topic ){
var tmp = topic.SubTopics.Topic;
if( tmp.length && tmp.length > 0 ){ //多个子节点
obj.children = [];
for(var i in tmp){
obj.children.push({});
processTopic(tmp[i], obj.children[i]);
}
}else{ //一个子节点
obj.children = [{}];
processTopic(tmp, obj.children[0]);
}
}
};
function xml2km(xml){
var json = $.xml2json(xml);
var result = {};
processTopic(json.OneTopic.Topic, result);
return result;
};
function getEntries(file, onend) {
zip.createReader(new zip.BlobReader(file), function(zipReader) {
zipReader.getEntries(onend);
}, onerror);
};
return {
fileDescription: 'mindmanager格式文件',
fileExtension: '.mmap',
decode: function ( local ) {
return {
then : function(local, callback){
getEntries( local, function( entries ) {
entries.forEach(function( entry ) {
if(entry.filename == 'Document.xml'){
entry.getData(new zip.TextWriter(), function(text) {
var km = xml2km($.parseXML(text));
callback && callback( km );
});
}
});
});
}
};
},
// recognize: recognize,
recognizePriority: -1
};
} );
...@@ -7,7 +7,104 @@ ...@@ -7,7 +7,104 @@
XMind files are generated in XMind Workbook (.xmind) format, an open format that is based on the principles of OpenDocument. It consists of a ZIP compressed archive containing separate XML documents for content and styles, a .jpg image file for thumbnails, and directories for related attachments. XMind files are generated in XMind Workbook (.xmind) format, an open format that is based on the principles of OpenDocument. It consists of a ZIP compressed archive containing separate XML documents for content and styles, a .jpg image file for thumbnails, and directories for related attachments.
*/ */
KityMinder.registerProtocal( 'xmind', { KityMinder.registerProtocal( 'xmind', function () {
var markerMap = {
'priority-1' : ['PriorityIcon', 1]
,'priority-2' : ['PriorityIcon', 2]
,'priority-3' : ['PriorityIcon', 3]
,'priority-4' : ['PriorityIcon', 4]
,'priority-5' : ['PriorityIcon', 5]
,'task-start' : ['ProgressIcon', 1]
,'task-quarter' : ['ProgressIcon', 2]
,'task-half' : ['ProgressIcon', 3]
,'task-3quar' : ['ProgressIcon', 4]
,'task-done' : ['ProgressIcon', 5]
,'task-oct' : null
,'task-3oct' : null
,'task-5oct' : null
,'task-7oct' : null
};
function processTopic(topic, obj){
//处理文本
obj.data = { text : topic.title };
// 处理标签
if(topic.marker_refs && topic.marker_refs.marker_ref){
var markers = topic.marker_refs.marker_ref;
if( markers.length && markers.length > 0 ){
for (var i in markers) {
var type = markerMap[ markers[i]['marker_id'] ];
type && (obj.data[ type[0] ] = type[1]);
}
}else{
var type = markerMap[ markers['marker_id'] ];
type && (obj.data[ type[0] ] = type[1]);
}
}
//处理子节点
if( topic.children && topic.children.topics && topic.children.topics.topic ){
var tmp = topic.children.topics.topic;
if( tmp.length && tmp.length > 0 ){ //多个子节点
obj.children = [];
for(var i in tmp){
obj.children.push({});
processTopic(tmp[i], obj.children[i]);
}
}else{ //一个子节点
obj.children = [{}];
processTopic(tmp, obj.children[0]);
}
}
};
function xml2km(xml){
var json = $.xml2json(xml);
var result = {};
processTopic(json.sheet.topic, result);
return result;
};
function getEntries(file, onend) {
zip.createReader(new zip.BlobReader(file), function(zipReader) {
zipReader.getEntries(onend);
}, onerror);
};
return {
fileDescription: 'xmind格式文件',
fileExtension: '.xmind', fileExtension: '.xmind',
decode: function ( local ) {
return {
then : function(local, callback){
getEntries( local, function( entries ) {
entries.forEach(function( entry ) {
if(entry.filename == 'content.xml'){
entry.getData(new zip.TextWriter(), function(text) {
var km = xml2km($.parseXML(text));
callback && callback( km );
});
}
});
});
}
};
},
// recognize: recognize,
recognizePriority: -1
};
} ); } );
...@@ -11,3 +11,6 @@ ...@@ -11,3 +11,6 @@
## 解析和生成目标 ## 解析和生成目标
只解析 KityMinder 支持部分的文件内容;只生成 KityMinder 和 XMind 都支持的文件内容 只解析 KityMinder 支持部分的文件内容;只生成 KityMinder 和 XMind 都支持的文件内容
使用zip.js解开xmind文件,根目录下的content.xml,描述脑图的结构,其中节点属性只有title对kityminder有用,以及children下面的topic(即节点)
将content.xml的结构和属性解析为xmind的km格式即可渲染
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment