Commit bbb2983c authored by Frank Bergmann's avatar Frank Bergmann

- OpenACS 5.9

parent 5babf6d2
......@@ -10,11 +10,13 @@
<version name="0.87d" url="http://openacs.org/repository/download/apm/ajaxhelper-0.87d.apm">
<owner url="mailto:ham@solutiongrove.com">Hamilton Chua</owner>
<summary>Provides helper procs to generate javascript used for Ajax and generating cinematic effects. Includes Scriptaculous 1.7.1 beta3 with Prototype 1.5.1, ExtJS 1.1.1 and the Yahoo UI Libraries (2.3.0). As of 0.87d, there is now an option to load YUI js source files direct from yahoo (http://developer.yahoo.com/yui/articles/hosting/). Lee Denison's template::head is now used to load javascript sources and css. The YUI loader is used to intelligently load YUI sources and css. As of 0.7d, all javascript libraries have been moved to ajaxhelper/www/resources to take advantage of caching. As of 0.8d, the wrappers will now be able to check a global variable to see if the required sources are loaded, this allows helper procs to automatically load the javascript sources you need.</summary>
<summary>Ajax Helper for various javascript libraries.</summary>
<description format="text/plain">Provides helper procs to generate javascript used for Ajax and generating cinematic effects. Includes Scriptaculous 1.7.1 beta3 with Prototype 1.5.1, ExtJS 1.1.1 and the Yahoo UI Libraries (2.3.0). As of 0.87d, there is now an option to load YUI js source files direct from yahoo (http://developer.yahoo.com/yui/articles/hosting/). Lee Denison's template::head is now used to load javascript sources and css. The YUI loader is used to intelligently load YUI sources and css. As of 0.7d, all javascript libraries have been moved to ajaxhelper/www/resources to take advantage of caching. As of 0.8d, the wrappers will now be able to check a global variable to see if the required sources are loaded, this allows helper procs to automatically load the javascript sources you need.</description>
<vendor>Solution Grove</vendor>
<maturity>0</maturity>
<maturity>1</maturity>
<provides url="ajaxhelper" version="0.87d"/>
<requires url="acs-kernel" version="5.8.1"/>
<callbacks>
</callbacks>
......
......@@ -71,9 +71,9 @@ ad_proc -public ah::ext::ajax {
} {
set script "Ext.Ajax.request({url:\"$url\""
if { [exists_and_not_null params] } { append script ",params:$params" }
if { [exists_and_not_null success] } { append script ",success:$success" }
if { [exists_and_not_null failure] } { append script ",failure:$failure" }
if { ([info exists params] && $params ne "") } { append script ",params:$params" }
if { ([info exists success] && $success ne "") } { append script ",success:$success" }
if { ([info exists failure] && $failure ne "") } { append script ",failure:$failure" }
append script "}); "
return $script
}
......
......@@ -52,7 +52,7 @@ ad_proc ah::lb::add_column_menu {
set groupbys [list]
foreach elm $groupby_ref(values) {
set value [lindex $elm 1]
lappend groupbys [lindex [lindex $value 0] 1]
lappend groupbys [lindex $value 0 1]
}
foreach {name value} [array get list_properties] {
......@@ -199,7 +199,7 @@ ad_proc ah::lb::add_add_column_menu {
}
}
if {[llength $addcolumnlist]} {
set actions_count [expr [llength $list_properties(actions)] / 3]
set actions_count [expr {[llength $list_properties(actions)] / 3}]
ah::yui::menu_from_list -varname "oMenuAddColumn" \
-id "menuAddColumn" \
-menulist $addcolumnlist \
......@@ -396,7 +396,7 @@ ad_proc ah::lb::prepare_template {
}
}
if {[exists_and_not_null __list_view]} {
if {([info exists __list_view] && $__list_view ne "")} {
ad_form -extend -name save_view -form {
{formbutton_delete:text(submit) {label "[_ acs-kernel.common_Delete]"}}
}
......@@ -406,13 +406,13 @@ ad_proc ah::lb::prepare_template {
element set_value save_view save_view $__list_view
}
} -on_submit {
if {[exists_and_not_null formbutton_delete]} {
if {([info exists formbutton_delete] && $formbutton_delete ne "")} {
ad_returnredirect [export_vars -base /ajax/list-view-delete {{list_name $___list_name} {view_name $__list_view} {return_url "[ad_conn url]"} {parent_id "[ad_conn package_id]}}]
ad_script_abort
}
}
if {[exists_and_not_null save_view]} {
if {([info exists save_view] && $save_view ne "")} {
set saved_view_id [template::list::view_save \
-list_name $___list_name \
-view_name $save_view \
......@@ -446,7 +446,7 @@ ad_proc ah::lb::prepare_list {
template::list::view_set_filter_vars \
-list_name $___list_name
set view_modified_p 0
if {[exists_and_not_null __list_view]} {
if {([info exists __list_view] && $__list_view ne "")} {
set view_name $__list_view
set view_title $__list_view
......
......@@ -164,7 +164,7 @@ ad_proc -public ah::js_sources {
}
# js_sources was called with no parameters, just load the defaults
if { $source == "default" } {
if { $source eq "default" } {
if { ![ah::is_js_sources_loaded -js_source "prototype"] } {
# load prototype
template::head::add_javascript -src "${ah_base_url}prototype/prototype${minsuffix}.js"
......@@ -320,7 +320,7 @@ ad_proc -private ah::enclose_in_script {
ad_proc -public ah::create_js_function {
-body:required
{-name ""}
{-parameters {} }
{-parameters "" }
} {
Helper procedure to generate a javascript function
@author Hamilton Chua (ham@solutiongrove.com)
......@@ -331,7 +331,7 @@ ad_proc -public ah::create_js_function {
@param parameters The comma separated list of parameters of the javascript function
} {
set script "function ${name} ("
if { [exists_and_not_null parameters] } { append script [join $parameters ","] }
if { $parameters ne "" } { append script [join $parameters ","] }
append script ") \{ $body \}"
return $script
}
......@@ -441,10 +441,10 @@ ad_proc -public ah::ajaxperiodical {
set preoptions "asynchronous:${asynchronous},frequency:${frequency},method:'post'"
if { [exists_and_not_null pars] } {
if { $pars ne "" } {
append preoptions ",parameters:$pars"
}
if { [exists_and_not_null options] } { append preoptions ",$options" }
if { $options ne "" } { append preoptions ",$options" }
set script "new Ajax.PeriodicalUpdater('$container','$url',{$preoptions}); "
return $script
......@@ -477,10 +477,10 @@ ad_proc -public ah::ajaxrequest {
set preoptions "asynchronous:${asynchronous},method:'post'"
if { [exists_and_not_null pars] } {
if { $pars ne "" } {
append preoptions ",parameters:$pars"
}
if { [exists_and_not_null options] } { append preoptions ",$options" }
if { $options ne "" } { append preoptions ",$options" }
set script "new Ajax.Request('$url',{$preoptions}); "
return $script
......@@ -535,12 +535,12 @@ ad_proc -public ah::ajaxupdate {
set preoptions "asynchronous:$asynchronous,method:'post'"
if { [exists_and_not_null pars] } {
if { $pars ne "" } {
append preoptions ",parameters:$pars"
}
if { [exists_and_not_null options] } { append preoptions ",$options" }
if { $options ne "" } { append preoptions ",$options" }
if { [exists_and_not_null effect] } {
if { $effect ne "" } {
set effects_script [ah::effects -element $container -effect $effect -options $effectopts -element_is_var]
append preoptions ",onSuccess: function(t) { $effects_script }"
}
......@@ -579,7 +579,7 @@ ad_proc -public ah::popup {
ah::requires -sources "overlibmws"
if { [exists_and_not_null options] } {
if { $options ne "" } {
set overlibopt ","
append overlibopt $options
} else {
......@@ -941,7 +941,7 @@ ad_proc -public ah::generate_autosuggest_array {
if {[llength $array_list]} {
set suggestion_list $array_list
} elseif {![string equal $sql_query {}]} {
} elseif {$sql_query ne {} } {
set suggestion_list [db_list_of_lists get_array_list $sql_query]
} else {
# just do something for failover
......
......@@ -20,7 +20,6 @@ ad_proc -private ah::yui::load_js_sources {
@author Hamilton Chua (ham@solutiongrove.com)
@creation-date 2006-11-05
@modified-date 2007-08-12
} {
set ah_base_url "[ah::get_url]yui/"
......@@ -89,10 +88,9 @@ ad_proc -private ah::yui::is_js_sources_loaded {
@author Hamilton Chua (ham@solutiongrove.com)
@creation-date 2006-11-05
} {
global ajax_helper_yui_js_sources
set state 0
if { [info exists ajax_helper_yui_js_sources] } {
foreach source $ajax_helper_yui_js_sources {
if { [info exists ::ajax_helper_yui_js_sources] } {
foreach source $::ajax_helper_yui_js_sources {
if { [string match $source $js_source] } {
set state 1
break
......@@ -123,7 +121,7 @@ ad_proc -private ah::yui::requires {
# - is the source "utilities" or "yahoo","dom","event"
if { ![ah::yui::is_js_sources_loaded -js_source $source] && [ah::yui::is_valid_source -js_source $source] } {
# source is utilities
if { $source == "utilities"} {
if { $source eq "utilities"} {
# load it only if yahoo, dom and event are not loaded
if { ![ah::yui::is_js_sources_loaded -js_source "yahoo"] && ![ah::yui::is_js_sources_loaded -js_source "dom"] && ![ah::yui::is_js_sources_loaded -js_source "event"]} {
lappend ajax_helper_yui_js_sources $source
......@@ -189,7 +187,7 @@ ad_proc -public ah::yui::js_sources {
}
# js_sources was called with no parameters, just load the defaults
if { $source == "default" } {
if { $source eq "default" } {
# yahoo has a compressed js file with yahoo, dom and event all in one file (utilities)
if { ![ah::is_js_sources_loaded -js_source "utilities"] } {
append script "<script type=\"text/javascript\" src=\"${ah_base_url}utilities/utilities.js\"></script> \n"
......@@ -396,7 +394,7 @@ ad_proc -public ah::yui::create_tree {
ah::yui::requires -sources "dom,treeview"
if { [exists_and_not_null css] } { template::head::add_css -href $css }
if { $css ne "" } { template::head::add_css -href $css }
set script "var ${varname} = new YAHOO.widget.TreeView(\"${element}\"); "
append script "var ${varname}root = ${varname}.getRoot(); "
......@@ -458,14 +456,14 @@ ad_proc -private ah::yui::create_tree_node {
} {
set script "var od${varname} = {label: \"${label}\", id: \"${varname}\", href: \"${href}\"}; "
if { [exists_and_not_null attach_to_node] } {
if { $attach_to_node ne "" } {
append script "var node = ${treevarname}.getNodeByProperty('id','${attach_to_node}'); "
append script "if ( node == null ) { var node = nd${attach_to_node}; } "
} else {
append script "var node = ${treevarname}root; "
}
if { ![exists_and_not_null open] } { set open "false" }
if { $open eq "" } { set open "false" }
append script "var nd${varname} = new YAHOO.widget.TextNode(od${varname},node,${open}); "
......@@ -473,7 +471,7 @@ ad_proc -private ah::yui::create_tree_node {
append script "var dd${varname} = new YAHOO.util.DDTarget(nd${varname}.labelElId); "
}
if { [exists_and_not_null dynamic_load] } {
if { $dynamic_load ne "" } {
append script "nd${varname}.setDynamicLoad(${dynamic_load}); "
}
......@@ -506,7 +504,7 @@ ad_proc -public ah::yui::menu_from_markup {
} {
ah::yui::requires -sources "menu,container,overlay"
if { [exists_and_not_null css] } { template::head::add_css -href $css }
if { $css ne "" } { template::head::add_css -href $css }
set script "${varname} = new YAHOO.widget.Menu(\"${markupid}\",{${options}}); "
append script "${varname}.render(); "
......@@ -545,7 +543,7 @@ ad_proc -public ah::yui::menu_list_to_json {
foreach row $lists_of_pairs {
set pairs [list]
foreach pair $row {
if { [lindex $pair 0] == "submenu" } {
if { [lindex $pair 0] eq "submenu" } {
set submenulist [lindex $pair 1]
set submenuid [lindex $submenulist 0]
......@@ -597,7 +595,7 @@ ad_proc -public ah::yui::menu_from_list {
ah::yui::requires -sources "event,menu,container,overlay"
if { [exists_and_not_null css] } { template::head::add_css -href $css }
if { $css ne "" } { template::head::add_css -href $css }
set jsonlist [ah::yui::menu_list_to_json -lists_of_pairs $menulist]
......@@ -643,12 +641,12 @@ ad_proc -public ah::yui::contextmenu {
ah::yui::requires -sources "menu,container,overlay"
if { [exists_and_not_null css] } { template::head::add_css -href $css }
if { $css ne "" } { template::head::add_css -href $css }
set jsonlist [ah::yui::menu_list_to_json -lists_of_pairs $menulist]
set initoptions "trigger: ${triggerel}, lazyload:true"
if { [exists_and_not_null options] } {
if { $options ne "" } {
set options "${initoptions},${options}"
} else {
set options "${initoptions}"
......
......@@ -195,7 +195,7 @@ proc json::_json2dict {{txtvar txt}} {
# but it may be 0.xxx
string is double -failindex last $txt
if {$last > 0} {
set num [string range $txt 0 [expr {$last - 1}]]
set num [string range $txt 0 $last-1]
set txt [string range $txt $last end]
switch -- $state {
TOP {
......
......@@ -23,7 +23,7 @@ ad_proc -public ad_return_template {
@param string If specified, will return the resulting page to the caller
string instead sending it to the connection.
} {
if {![empty_string_p $template]} {
if {$template ne ""} {
template::set_file \
[template::util::url_to_file $template [ad_conn file]]
}
......@@ -88,7 +88,7 @@ ad_proc -public ad_return_exception_template {
ad_proc -public get_server_root {} {
Get the server root directory (supposing we run under ACS)
} {
file dir [ns_info tcllib]
file dirname $::acs::tcllib
}
......@@ -96,12 +96,12 @@ ad_proc adp_parse_ad_conn_file {} {
handle a request for an adp and/or tcl file in the template system.
} {
namespace eval template variable parse_level ""
#ns_log debug "adp_parse_ad_conn_file => file '[file root [ad_conn file]]'"
#ns_log debug "adp_parse_ad_conn_file => file '[file rootname [ad_conn file]]'"
template::reset_request_vars
set parsed_template [template::adp_parse [file root [ad_conn file]] {}]
set parsed_template [template::adp_parse [file rootname [ad_conn file]] {}]
if {![empty_string_p $parsed_template]} {
if {$parsed_template ne ""} {
#
# acs-lang translator mode
......@@ -124,9 +124,9 @@ ad_proc adp_parse_ad_conn_file {} {
if { [string first "</select" [string tolower $select]] != -1 } {
set start [lindex $indices 1]
} else {
set before [string range $parsed_template 0 [expr [lindex $indices 0]-1]]
set before [string range $parsed_template 0 [lindex $indices 0]-1]
set message [string range $parsed_template [lindex $message_idx 0] [lindex $message_idx 1]]
set after [string range $parsed_template [expr [lindex $indices 1] + 1] end]
set after [string range $parsed_template [lindex $indices 1]+1 end]
set parsed_template "${before}${message}${select}${after}"
}
}
......@@ -134,8 +134,8 @@ ad_proc adp_parse_ad_conn_file {} {
# TODO: We could also move message keys out of <head>...</head>
while { [regexp -indices {\x002\(\x001([^\x001]*)\x001\)\x002} $parsed_template indices key] } {
set before [string range $parsed_template 0 [expr [lindex $indices 0] - 1]]
set after [string range $parsed_template [expr [lindex $indices 1] + 1] end]
set before [string range $parsed_template 0 [lindex $indices 0]-1]
set after [string range $parsed_template [lindex $indices 1]+1 end]
set key [string range $parsed_template [lindex $key 0] [lindex $key 1]]
......
......@@ -59,7 +59,7 @@ ad_page_contract {
$Id$
}
if {[template::util::is_nil doc(type)]} {
if {![info exists doc(type)]} {
set doc(type) {<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">}
}
......@@ -67,7 +67,7 @@ if {![info exists doc(title)]} {
set doc(title) "[ad_conn instance_name]"
ns_log warning "[ad_conn url] has no doc(title) set."
}
if {[template::util::is_nil doc(charset)]} {
if {![info exists doc(charset)]} {
set doc(charset) [ns_config ns/parameters OutputCharset [ad_conn charset]]
}
......
......@@ -11,13 +11,13 @@ ad_page_contract {
#
set system_name [ad_system_name]
if { [string equal [ad_conn url] "/"] } {
if {[ad_conn url] eq "/"} {
set system_url ""
} else {
set system_url [ad_url]
}
if {[template::util::is_nil title]} {
if {![info exists title]} {
# TODO: decide how best to set the lang attribute for the title
set title [ad_conn instance_name]
}
......@@ -114,7 +114,7 @@ util_get_user_messages -multirow user_messages
set acs_lang_url [apm_package_url_from_key "acs-lang"]
set num_of_locales [llength [lang::system::get_locales]]
if {[empty_string_p $acs_lang_url]} {
if {$acs_lang_url eq ""} {
set lang_admin_p 0
} else {
set lang_admin_p [permission::permission_p \
......
This diff is collapsed.
......@@ -4,7 +4,7 @@ ad_page_contract {
list_name
view_name
return_url
parent_id
parent_id:naturalnum,notnull
}
set name "template:list:${list_name}:view:${view_name}"
......
/*
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://www.extjs.com/license
*/
Ext.dd.DropZone=function(B,A){Ext.dd.DropZone.superclass.constructor.call(this,B,A)};Ext.extend(Ext.dd.DropZone,Ext.dd.DropTarget,{getTargetFromEvent:function(A){return Ext.dd.Registry.getTargetFromEvent(A)},onNodeEnter:function(D,A,C,B){},onNodeOver:function(D,A,C,B){return this.dropAllowed},onNodeOut:function(D,A,C,B){},onNodeDrop:function(D,A,C,B){return false},onContainerOver:function(A,C,B){return this.dropNotAllowed},onContainerDrop:function(A,C,B){return false},notifyEnter:function(A,C,B){return this.dropNotAllowed},notifyOver:function(A,C,B){var D=this.getTargetFromEvent(C);if(!D){if(this.lastOverNode){this.onNodeOut(this.lastOverNode,A,C,B);this.lastOverNode=null}return this.onContainerOver(A,C,B)}if(this.lastOverNode!=D){if(this.lastOverNode){this.onNodeOut(this.lastOverNode,A,C,B)}this.onNodeEnter(D,A,C,B);this.lastOverNode=D}return this.onNodeOver(D,A,C,B)},notifyOut:function(A,C,B){if(this.lastOverNode){this.onNodeOut(this.lastOverNode,A,C,B);this.lastOverNode=null}},notifyDrop:function(A,C,B){if(this.lastOverNode){this.onNodeOut(this.lastOverNode,A,C,B);this.lastOverNode=null}var D=this.getTargetFromEvent(C);return D?this.onNodeDrop(D,A,C,B):this.onContainerDrop(A,C,B)},triggerCacheRefresh:function(){Ext.dd.DDM.refreshCache(this.groups)}});
\ No newline at end of file
Ext JS - JavaScript Library
Copyright (c) 2006-2007, Ext JS, LLC
Copyright (c) 2006-2009, Ext JS, LLC
All rights reserved.
licensing@extjs.com
http://extjs.com/license
License of CSS and Graphics ("Assets")
Open Source License
------------------------------------------------------------------------------------------
The Assets distributed with Ext are licensed for use ONLY
with their associated Ext JavaScript component ("Component"). Use of the Assets in
any way that does not also include the Component is prohibited without explicit
permission from Ext JS, LLC. Deriving images and CSS from the Assets in an effort
to bypass this license is also prohibited.
Ext is licensed under the terms of the Open Source GPL 3.0 license.
http://www.gnu.org/licenses/gpl.html
Open Source License
------------------------------------------------------------------------------------------
Ext is also licensed under the terms of the Open Source LGPL 3.0 license. You may use
our open source license if you:
* Want to use Ext in an open source project that precludes using non-open source software
* Plan to use Ext in a personal, educational or non-profit manner
* Are using Ext in a commercial application that is not a software development library
or toolkit, you will meet LGPL requirements and you do not wish to support the project
There are several FLOSS exceptions available for use with this release for
open source applications that are distributed under a license other than the GPL.
http://www.gnu.org/licenses/lgpl.html
* Open Source License Exception for Applications
http://extjs.com/products/floss-exception.php
* Open Source License Exception for Development
http://extjs.com/products/ux-exception.php
Commercial License
------------------------------------------------------------------------------------------
If you are using this library for commercial purposes, we encourage you to purchase
a commercial license. Please visit http://extjs.com/license for more details.
This is the appropriate option if you are creating proprietary applications and you are
not prepared to distribute and share the source code of your application under the
GPL v3 license. Please visit http://extjs.com/license for more details.
OEM / Reseller License
------------------------------------------------------------------------------------------
If you plan to distribute Ext in a product that will be packaged or sold as a software
development library, toolkit or plug-in-based framework ("LIBRARY"), we require that you
work with us to establish a specific license that is appropriate. Use of the open source
license in a LIBRARY is not permitted without explicit permission from Ext JS, LLC.
For more details, please visit: http://extjs.com/license.
--
......@@ -47,4 +39,4 @@ For more details, please visit: http://extjs.com/license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
\ No newline at end of file
General Public License for more details.
\ No newline at end of file
/*
* Ext JS Library 2.0 RC 1
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
/*
* Portuguese/Brazil Translation by Weber Souza
* 08 April 2007
......
<master>
<property name="title"> TEST : Ajax </property>
<property name="doc(title)"> TEST : Ajax </property>
Type your name please : <input type="text" id="name" />
<br>
......
<master>
<property name="title"> TEST : Dojo Chart Engine </property>
<property name="doc(title)"> TEST : Dojo Chart Engine </property>
<pre>
@testjs;noquote@
......
<master>
<property name="title"> TEST : Dojo IO Bind </property>
<property name="doc(title)"> TEST : Dojo IO Bind </property>
<p>Click the button to fetch some text via AJAX using Dojo.IO.Bind
<br /><input type="button" id="go" value="Go" onclick="testio()">
......
<master>
<property name="title">TEST : Drag n Drop</property>
<property name="doc(title)">TEST : Drag n Drop</property>
<div id="test" class="portlet" style="position:absolute;
background-color:gray;
......
<master>
<property name="title"> Test Effects </property>
<property name="doc(title)"> Test Effects </property>
<a href="javascript:void(0)" onclick="@showscript;noquote@"> Show </a> | <a href="javascript:void(0)" onclick="@fadescript;noquote@"> Hide </a>
<br /><br />
......
<master>
<property name="title">Progress Bar</property>
<property name="doc(title)">Progress Bar</property>
@script;noquote@
\ No newline at end of file
<master>
<property name="title"> TEST : DOM Insert </property>
<property name="doc(title)"> TEST : DOM Insert </property>
<div id="maindiv">The quick brown fox jumped over the </div>
<br>
......
<master>
<property name="title"> TEST : Rounded Corners </property>
<property name="doc(title)"> TEST : Rounded Corners </property>
<property name="head">
<style>
.myBox{
......
<master>
<property name="title"> TEST : Yahoo Context Menu</property>
<property name="doc(title)"> TEST : Yahoo Context Menu</property>
<p>Right click anywhere on this page.
\ No newline at end of file
<master>
<property name="title"> TEST : Yahoo Menu from TCL List</property>
<property name="doc(title)"> TEST : Yahoo Menu from Tcl List</property>
<property name="head">
<style>
#samplemenu li {
......
<master>
<property name="title"> TEST : Yahoo Menu from Markup</property>
<property name="doc(title)"> TEST : Yahoo Menu from Markup</property>
<center><a href="javascript:void(0)" id="showmenu1">Show Menu 1</a>&nbsp;<a href="javascript:void(0)" id="showmenu2">Show Menu 2</a></center>
......
<master>
<property name="title"> TEST : Yahoo Tree View </property>
<property name="doc(title)"> TEST : Yahoo Tree View </property>
<div id="folders"></div>
\ No newline at end of file
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