diff --git a/src/core/BaseDialog.coffee b/src/core/BaseDialog.coffee index 50645ee..d2f3f14 100644 --- a/src/core/BaseDialog.coffee +++ b/src/core/BaseDialog.coffee @@ -271,11 +271,13 @@ class SelectionDialog extends BasicDialog super.init() me = @ (@find "list").set "data", @data.data if @data and @data.data - (@find "btnOk").set "onbtclick", (e) -> + fn = (e) -> data = (me.find "list").get "selectedItem" return me.notify __("Please select an item") unless data me.handle(data.get("data")) if me.handle me.quit() + (@find "list").set "onlistdbclick", fn + (@find "btnOk").set "onbtclick", fn (@find "btnCancel").set "onbtclick", (e) -> me.quit() @@ -384,7 +386,8 @@ class FileDialog extends BasicDialog (@find "bt-ok").set "onbtclick", (e) -> f = fileview.get "selectedFile" return me.notify __("Please select a file/fofler") unless f - return me.notify __("Please select {0} only", me.data.type) if me.data and me.data.type and me.data.type isnt f.type + if me.data and me.data.type and me.data.type isnt f.type + return me.notify __("Please select {0} only", me.data.type) if me.data and me.data.mimes #verify the mime m = false diff --git a/src/core/api.coffee b/src/core/api.coffee index 52c4f03..9aa71b6 100644 --- a/src/core/api.coffee +++ b/src/core/api.coffee @@ -295,28 +295,31 @@ Ant.OS.API = requires: (l) -> new Promise (resolve, reject) -> if not Ant.OS.API.shared[l] - link = l - if not l.match /^(https?:\/\/[^\s]+)/g - path = "os://scripts/" - cssFile = "#{path}#{l}.css".asFileHandle() - cssFile.onready() + libfp = l.asFileHandle() + switch libfp.ext + when "css" + libfp.onready() + .then () -> + $('', { + rel: 'stylesheet', + type: 'text/css', + 'href': "#{libfp.getlink()}" + }) + .appendTo 'head' + Ant.OS.API.shared[l] = true + console.log "loaded css:", l + Ant.OS.announcer.trigger "sharedlibraryloaded", l + .catch (e) -> reject e + when "js" + Ant.OS.API.script libfp.getlink() .then () -> - $('', { - rel: 'stylesheet', - type: 'text/css', - 'href': "#{cssFile.getlink()}" - }) - .appendTo 'head' + Ant.OS.API.shared[l] = true + console.log "loaded javascript:", l + Ant.OS.announcer.trigger "sharedlibraryloaded", l + resolve(l) .catch (e) -> - js = "#{path}#{l}.js" - link = js.asFileHandle().getlink() - Ant.OS.API.script link - .then () -> - Ant.OS.API.shared[l] = true - console.log "loaded:", l - Ant.OS.announcer.trigger "sharedlibraryloaded", l - resolve(l) - .catch (e) -> + reject e + else reject e else console.log l, "Library exist, no need to load" diff --git a/src/core/gui.coffee b/src/core/gui.coffee index 234e6fc..a4f5c61 100644 --- a/src/core/gui.coffee +++ b/src/core/gui.coffee @@ -119,9 +119,11 @@ Ant.OS.GUI = return Ant.OS.announcer.osinfo __("No application available to open {0}", it.filename) if apps.length is 0 return Ant.OS.GUI.launch apps[0].app, [it.path] if apps.length is 1 list = ( { text: e.app, icon: e.icon, iconclass: e.iconclass } for e in apps ) - Ant.OS.GUI.openDialog "SelectionDialog", ( d ) -> - Ant.OS.GUI.launch d.text, [it.path] - , __("Open with"), list + Ant.OS.GUI.openDialog("SelectionDialog", { + title: __("Open with"), + data: list + }).then ( d ) -> + Ant.OS.GUI.launch d.text, [ { path: it.path, type: it.type }] forceLaunch: (app, args) -> console.warn "This method is used for developing only, please use the launch method instead" @@ -377,6 +379,7 @@ Ant.OS.GUI = return Ant.OS.GUI.openWith it.get("data") if it it = Ant.OS.setting.desktop.path.asFileHandle() it.mime = "dir" + it.type = "dir" Ant.OS.GUI.openWith it when "desktop-refresh" desktop[0].fetch() diff --git a/src/core/tags/FileViewTag.coffee b/src/core/tags/FileViewTag.coffee index 96d7e59..4433863 100644 --- a/src/core/tags/FileViewTag.coffee +++ b/src/core/tags/FileViewTag.coffee @@ -9,6 +9,7 @@ class FileViewTag extends Ant.OS.GUI.BaseTag @setopt "showhidden", false @setopt "fetch", undefined @setopt "path", undefined + @setopt "chdir", true @setopt "view", "list" @preventUpdate = false @header = [ @@ -150,6 +151,9 @@ class FileViewTag extends Ant.OS.GUI.BaseTag @refs.status.set("text", " ") if @get "status" fileselect: (e) -> + if e.path is @get "path" + e.type = "dir" + e.mime = "dir" if @get "status" @refs.status.set "text", __( "Selected: {0} ({1} bytes)", @@ -161,7 +165,10 @@ class FileViewTag extends Ant.OS.GUI.BaseTag @observable.trigger "fileselect", evt filedbclick: (e) -> - if e.type is "dir" + if e.path is @get "path" + e.type = "dir" + e.mime = "dir" + if e.type is "dir" and @get "chdir" @set "path", e.path else evt = { id: @aid(), data: e } diff --git a/src/core/tags/ListViewTag.coffee b/src/core/tags/ListViewTag.coffee index 2d2126f..063da64 100644 --- a/src/core/tags/ListViewTag.coffee +++ b/src/core/tags/ListViewTag.coffee @@ -60,6 +60,9 @@ class SimpleListItemTag extends ListViewItemTag @set "selected", v.selected if v.selected @set "closable", v.closable if v.closable + update: () -> + @set "data", @get("data") + itemlayout: () -> { el: "afx-label", ref: "label" } diff --git a/src/core/tags/TabBarTag.coffee b/src/core/tags/TabBarTag.coffee index f018b1e..8e501c0 100644 --- a/src/core/tags/TabBarTag.coffee +++ b/src/core/tags/TabBarTag.coffee @@ -3,9 +3,13 @@ class TabBarTag extends Ant.OS.GUI.BaseTag super r, o @setopt "closable", false @setopt "ontabselect", (e) -> + @setopt "ontabclose", (e) -> @setopt "items", [] + @setopt "selected", -1 me = @ - @root.push = (e) -> me.refs.list.push e + @root.push = (e) -> + e.closable = me.get "closable" + me.refs.list.push e @root.remove = (e) -> me.refs.list.remove e @root.unshift = (e) -> me.refs.list.unshift e @refs.list.set "onlistselect", (e) -> @@ -13,14 +17,18 @@ class TabBarTag extends Ant.OS.GUI.BaseTag me.observable.trigger "tabselect", e __items__: (v) -> + i.closable = @get "closable" for i in v @refs.list.set "data", v + __selected__: (v) -> + @refs.list.set "selected", v + mount: () -> me = @ - @root.push = (e) -> me.refs.list.push e - @root.unshift = (e) -> me.refs.list.unshift e - @root.remove = (e) -> me.refs.list.remove e $(@refs.list).css "height", "100%" + @refs.list.set "onitemclose", (e) -> + e.id = me.aid() + me.get("ontabclose") e layout: () -> [{ diff --git a/src/packages/CodePad/assets/scheme.html b/src/packages/CodePad/assets/scheme.html index 34ecef8..541e1fd 100644 --- a/src/packages/CodePad/assets/scheme.html +++ b/src/packages/CodePad/assets/scheme.html @@ -1,19 +1,20 @@ - + - - + +
+ + +
- +
- - - - +
+ + +
\ No newline at end of file diff --git a/src/packages/CodePad/coffees/CommandPalette.coffee b/src/packages/CodePad/coffees/CommandPalette.coffee index a7e2160..64a3442 100644 --- a/src/packages/CodePad/coffees/CommandPalette.coffee +++ b/src/packages/CodePad/coffees/CommandPalette.coffee @@ -17,7 +17,7 @@ class CommandPalette extends this.OS.GUI.BasicDialog $(document).on "mousedown", cb $(me.find "searchbox").focus() @cmdlist = @find("container") - @cmdlist.set "data", @data if @data + @cmdlist.set "data", (v for v in @data.child) if @data $(@cmdlist).click (e) -> me.selectCommand() @@ -27,6 +27,10 @@ class CommandPalette extends this.OS.GUI.BasicDialog search: (e) -> switch e.which + when 27 + # escape key + @quit() + @data.parent.run(@parent) if @data.parent and @data.parent.run when 37 e.preventDefault() when 38 @@ -42,20 +46,20 @@ class CommandPalette extends this.OS.GUI.BasicDialog @selectCommand() else text = @searchbox.value - @cmdlist.set "data", @data if text.length is 2 + @cmdlist.set "data", (v for v in @data.child) if text.length is 2 return if text.length < 3 result = [] term = new RegExp text, 'i' - result.push v for v in @data when v.text.match term + result.push v for v in @data.child when v.text.match term @cmdlist.set "data", result selectCommand: () -> el = @cmdlist.get "selectedItem" return unless el - @quit() - @handle { data: { item: el } } if @handle - + result = false + result = @handle { data: { item: el } } if @handle + return @quit() unless result CommandPalette.scheme = """ @child = [] + @parent = undefined @select = (e) -> addAction: (v) -> + v.parent = @ @child.push v @ @@ -16,7 +18,7 @@ class CMDMenu run: (root) -> me = @ - root.openDialog(new CommandPalette(), @child) + root.openDialog(new CommandPalette(), @) .then (d) -> data = d.data.item.get("data") return data.run root if data.run @@ -35,10 +37,23 @@ CMDMenu.fromMenu = (mn) -> class CodePad extends this.OS.GUI.BaseApplication constructor: (args) -> super "CodePad", args - + @currfile = "Untitled".asFileHandle() + @currdir = undefined + if @args and @args.length > 0 + if @args[0].type is "dir" + @currdir = @args[0].path.asFileHandle() + else + @currfile = @args[0].path.asFileHandle() + @currdir = @currfile.parent() + main: () -> me = @ @fileview = @find("fileview") + @sidebar = @find("sidebar") + @tabbar = @find "tabbar" + @langstat = @find "langstat" + @editorstat = @find "editorstat" + @fileview.set "fetch", (path) -> new Promise (resolve, reject) -> dir = path @@ -47,7 +62,6 @@ class CodePad extends this.OS.GUI.BaseApplication return reject d.error if d.error me.currdir = dir resolve d.result - @fileview.set "path", "desktop://" @setup() setup: () -> @@ -61,12 +75,47 @@ class CodePad extends this.OS.GUI.BaseApplication enableLiveAutocompletion: true, highlightActiveLine: true, useWrapMode: true, - fontSize: "9pt" + fontSize: "11pt" } #themes = ace.require "ace/ext/themelist" @editor.setTheme "ace/theme/monokai" + @modes = ace.require "ace/ext/modelist" @editor.completers.push { getCompletions: ( editor, session, pos, prefix, callback ) -> } @editor.getSession().setUseWrapMode true + @editormux = false + @editor.on "input", () -> + if me.editormux + me.editormux = false + return false + if not me.currfile.dirty + me.currfile.dirty = true + me.currfile.text += "*" + me.tabbar.update() + @editor.getSession().selection.on "changeCursor", (e) -> + me.updateStatus() + + @tabbar.set "ontabselect", (e) -> + me.selecteTab $(e.data.item).index() + @tabbar.set "ontabclose", (e) -> + it = e.data.item + return false unless it + return me.closeTab it unless it.get("data").dirty + me.openDialog("YesNoDialog", { + title: __("Close tab"), + text: __("Close without saving ?") + }).then (d) -> + return me.closeTab it if d + me.editor.focus() + return false + @fileview.set "onfileopen", (e) -> + return if e.data.type is "dir" + me.openFile e.data.path.asFileHandle() + + @fileview.set "onfileselect", (e) -> + return unless e.data or e.data.type is "dir" + i = me.findTabByFile e.data.path.asFileHandle() + return me.tabbar.set "selected", i if i isnt -1 + @on "resize", () -> me.editor.resize() @on "focus", () -> me.editor.focus() @spotlight = new CMDMenu __("Command palette") @@ -78,8 +127,99 @@ class CodePad extends this.OS.GUI.BaseApplication me.spotlight.run me }] m.show e + + @bindKey "ALT-N", () -> me.menuAction "new" + @bindKey "ALT-O", () -> me.menuAction "open" + @bindKey "ALT-F", () -> me.menuAction "opendir" + @bindKey "CTRL-S", () -> me.menuAction "save" + @bindKey "ALT-W", () -> me.menuAction "saveas" + @initCommandPalete() - @editor.resize() + @initSideBar() + @openFile @currfile + + + openFile: (file) -> + #find tab + i = @findTabByFile file + return @tabbar.set "selected", i if i isnt -1 + return @newTab file if file.path.toString() is "Untitled" + me = @ + file.read() + .then (d) -> + file.cache = d or "" + me.newTab file + .catch (e) -> + me.error __("Unable to open: {0}", file.path) + + findTabByFile: (file) -> + lst = @tabbar.get "items" + its = ( i for d, i in lst when d.hash() is file.hash() ) + return -1 if its.length is 0 + return its[0] + + newTab: (file) -> + file.text = if file.basename then file.basename else file.path + file.cache = "" unless file.cache + file.um = new ace.UndoManager() + @currfile.selected = false + file.selected = true + #console.log cnt + @tabbar.push file + + closeTab: (it) -> + @tabbar.remove it + cnt = @tabbar.get("items").length + + if cnt is 0 + @openFile "Untitled".asFileHandle() + return false + @tabbar.set "selected", cnt - 1 + return false + + selecteTab: (i) -> + #return if i is @tabbar.get "selidx" + file = (@tabbar.get "items")[i] + return unless file + @scheme.set "apptitle", file.text.toString() + #return if file is @currfile + if @currfile isnt file + @currfile.cache = @editor.getValue() + @currfile.cursor = @editor.selection.getCursor() + @currfile.selected = false + @currfile = file + + if not file.langmode + if file.path.toString() isnt "Untitled" + m = @modes.getModeForPath(file.path) + file.langmode = { caption: m.caption, mode: m.mode } + else + file.langmode = { caption: "Text", mode: "ace/mode/text" } + @editormux = true + @editor.setValue file.cache, -1 + @editor.session.setMode file.langmode.mode + @editor.session.setUndoManager file.um + if file.cursor + @editor.renderer.scrollCursorIntoView { + row: file.cursor.row, column: file.cursor.column + }, 0.5 + @editor.selection.moveTo file.cursor.row, file.cursor.column + @updateStatus() + @editor.focus() + + updateStatus: () -> + c = @editor.session.selection.getCursor() + l = @editor.session.getLength() + @editorstat.set "text", __("Row {0}, col {1}, lines: {2}", c.row + 1, c.column + 1, l) + @langstat.set "text", @currfile.langmode.caption + + initSideBar: () -> + if @currdir + $(@sidebar).show() + @fileview.set "path", @currdir.path + else + $(@sidebar).hide() + @trigger "resize" addAction: (action) -> @spotlight.addAction action @@ -98,13 +238,13 @@ class CodePad extends this.OS.GUI.BaseApplication r.editor.setTheme data.theme r.editor.focus() @spotlight.addAction cmdtheme - - modes = ace.require "ace/ext/modelist" cmdmode = new CMDMenu __("Change language mode") - cmdmode.addAction { text: v.name, mode: v.mode } for v in modes.modes + cmdmode.addAction { text: v.caption, mode: v.mode } for v in @modes.modes cmdmode.onchildselect (d, r) -> data = d.data.item.get("data") r.editor.session.setMode data.mode + r.currfile.langmode = { caption: data.text, mode: data.mode } + r.updateStatus() r.editor.focus() @spotlight.addAction cmdmode @addAction CMDMenu.fromMenu @fileMenu() @@ -116,13 +256,83 @@ class CodePad extends this.OS.GUI.BaseApplication child: [ { text: __("New"), dataid: "new", shortcut: "A-N" }, { text: __("Open"), dataid: "open", shortcut: "A-O" }, + { text: __("Open Folder"), dataid: "opendir", shortcut: "A-F" }, { text: __("Save"), dataid: "save", shortcut: "C-S" }, { text: __("Save as"), dataid: "saveas", shortcut: "A-W" } ], onchildselect: (e, r) -> - console.log e.data.item.get "data" + me.menuAction e.data.item.get("data").dataid, r } - + + save: (file) -> + me = @ + file.write("text/plain") + .then (d) -> + return me.error __("Error saving file {0}: {1}", file.basename, d.error) if d.error + file.dirty = false + file.text = file.basename + me.tabbar.update() + me.scheme.set "apptitle", "#{me.currfile.basename}" + .catch (e) -> me.error e.stack + + + saveAs: () -> + me = @ + me.openDialog("FileDialog", { + title: __("Save as"), + file: me.currfile + }) + .then (f) -> + d = f.file.path.asFileHandle() + d = d.parent() if f.file.type is "file" + me.currfile.setPath "#{d.path}/#{f.name}" + me.save me.currfile + .catch (e) -> + me.error e.stack + + menuAction: (dataid, r) -> + me = @ + me = r if r + switch dataid + when "new" + me.openFile "Untitled".asFileHandle() + when "open" + me.openDialog("FileDialog", { + title: __("Open file"), + mimes: (v for v in me.meta().mimes when v isnt "dir") + }) + .then (f) -> + me.openFile f.file.path.asFileHandle() + when "opendir" + me.openDialog("FileDialog", { + title: __("Open folder"), + mimes: ["dir"] + }) + .then (f) -> + me.currdir = f.file.path.asFileHandle() + me.initSideBar() + when "save" + me.currfile.cache = me.editor.getValue() + return me.save me.currfile if me.currfile.basename + me.saveAs() + when "saveas" + me.currfile.cache = me.editor.getValue() + me.saveAs() + else + console.log dataid + + cleanup: (evt) -> + dirties = ( v for v in @tabbar.get "items" when v.dirty ) + return if dirties.length is 0 + me = @ + evt.preventDefault() + @.openDialog("YesNoDialog", { + title: "__(Quit)", + text: __("Ignore all {0} unsaved files ?", dirties.length) + }).then (d) -> + if d + v.dirty = false for v in dirties + me.quit() menu: () -> me = @ @@ -140,9 +350,9 @@ class CodePad extends this.OS.GUI.BaseApplication menu CodePad.dependencies = [ - "ace/ace", - "ace/ext-language_tools", - "ace/ext-modelist", - "ace/ext-themelist" + "os://scripts/ace/ace.js", + "os://scripts/ace/ext-language_tools.js", + "os://scripts/ace/ext-modelist.js", + "os://scripts/ace/ext-themelist.js" ] this.OS.register "CodePad", CodePad \ No newline at end of file diff --git a/src/packages/CodePad/css/main.css b/src/packages/CodePad/css/main.css index bd01cbd..c25edfd 100644 --- a/src/packages/CodePad/css/main.css +++ b/src/packages/CodePad/css/main.css @@ -1,36 +1,68 @@ -afx-app-window[data-id = "notepad"] .afx-window-wrapper afx-file-view{ +afx-app-window[data-id = "codepad"] afx-tab-bar> afx-list-view > div.list-container +{ + border-top: 1px solid #272822; + overflow: hidden; + font-size: 12px; +} + +afx-app-window[data-id = "codepad"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li.selected, +afx-app-window[data-id = "codepad"] afx-tab-bar> afx-list-view > div.list-container > ul > afx-list-item > li.selected{ + background-color:#272822; + color:white; + border: 0; + border-radius: 0; +} +afx-app-window[data-id = "codepad"] afx-tab-bar> afx-list-view afx-list-view i.closable:before { + color:afafaf; +} +afx-app-window[data-id = "codepad"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li, +afx-app-window[data-id = "codepad"] afx-tab-bar> afx-list-view > div.list-container > ul li{ + background-color:#333333; + color:#afafaf; + border-radius: 0; + border: 0; + padding-top: 5px; + padding-bottom: 5px; + padding-right: 20px; + border-right: 1px solid #272822; +} +afx-app-window[data-id = "codepad"] .afx-window-wrapper afx-vbox[data-id = "sidebar"]{ background-color:#272822; } -afx-app-window[data-id = "notepad"] div.afx-window-content { - background-color:#2d2d2d; +afx-app-window[data-id = "codepad"] div.afx-window-content { + background-color:#333333; } -afx-app-window[data-id = "notepad"] afx-resizer { +afx-app-window[data-id = "codepad"] afx-resizer { background-color:#272822; border-right: 1px solid #37373d; } -afx-app-window[data-id = "notepad"] .afx-window-wrapper .treecontainer{ - padding-top: 10px; -} -afx-app-window[data-id = "notepad"] .afx-window-wrapper afx-tree-view{ +afx-app-window[data-id = "codepad"] .afx-window-wrapper afx-tree-view{ color: white; padding: 0; } -afx-app-window[data-id = "notepad"] .afx-window-wrapper afx-tree-view afx-tree-view-item ul li{ +afx-app-window[data-id = "codepad"] .afx-window-wrapper afx-tree-view afx-tree-view-item ul li{ padding-left: 10px; } -afx-app-window[data-id = "notepad"] .afx-window-wrapper .afx_tree_item_selected ul{ +afx-app-window[data-id = "codepad"] .afx-window-wrapper .afx_tree_item_selected ul{ background-color: #116cd6; } -afx-app-window[data-id = "notepad"] afx-file-view afx-tree-view .afx-tree-view-item:before{ +afx-app-window[data-id = "codepad"] afx-file-view afx-tree-view .afx-tree-view-item:before{ color: white; } -afx-app-window[data-id = "notepad"] .afx-window-wrapper afx-hbox[data-id="bottom-hbox"]{ +afx-app-window[data-id = "codepad"] .afx-window-wrapper div[data-id="statctn"]{ color: white; background-color: #007acc; + padding-right: 10px; + padding-top: 5px; + font-size: 11px; } +afx-app-window[data-id = "codepad"] .afx-window-wrapper div[data-id="statctn"] afx-label { + float: right; + padding-left: 10px; +} afx-app-window[data-id = "cmd-win"] .afx-window-wrapper{ border-radius: 0px; diff --git a/src/packages/CodePad/package.json b/src/packages/CodePad/package.json index c8bcdac..3b368fc 100644 --- a/src/packages/CodePad/package.json +++ b/src/packages/CodePad/package.json @@ -14,6 +14,7 @@ "text/.*", "[^/]*/json.*", "[^/]*/.*ml", - "[^/]*/javascript" + "[^/]*/javascript", + "dir" ] } \ No newline at end of file diff --git a/src/packages/CoreServices/PushNotification.coffee b/src/packages/CoreServices/PushNotification.coffee index 0d77e0d..b00cec7 100644 --- a/src/packages/CoreServices/PushNotification.coffee +++ b/src/packages/CoreServices/PushNotification.coffee @@ -78,7 +78,7 @@ class PushNotification extends this.OS.GUI.BaseService icon: o.data.icon, iconclass: o.data.iconclass, closable: true } - console.log o + console.log o.data.e @mlist.unshift d @notifeed d diff --git a/src/packages/Files/main.coffee b/src/packages/Files/main.coffee index c2845e6..72a1965 100644 --- a/src/packages/Files/main.coffee +++ b/src/packages/Files/main.coffee @@ -27,7 +27,7 @@ class Files extends this.OS.GUI.BaseApplication @navinput = @find "navinput" @navbar = @find "nav-bar" if @args and @args.length > 0 - @currdir = @args[0].asFileHandle() + @currdir = @args[0].path.asFileHandle() else @currdir = "home://".asFileHandle() @favo = @find "favouri" diff --git a/src/packages/MarkOn/main.coffee b/src/packages/MarkOn/main.coffee index b61e04a..8115e58 100644 --- a/src/packages/MarkOn/main.coffee +++ b/src/packages/MarkOn/main.coffee @@ -26,7 +26,7 @@ class MarkOn extends this.OS.GUI.BaseApplication @container = @find "mycontainer" @previewOn = false if @args and @args.length > 0 - @currfile = @args[0].asFileHandle() + @currfile = @args[0].path.asFileHandle() else @currfile = "Untitled".asFileHandle() @editormux = false @@ -165,6 +165,9 @@ class MarkOn extends this.OS.GUI.BaseApplication me.quit() , __("Quit"), { text: __("Quit without saving ?") } -MarkOn.dependencies = [ "mde/simplemde.min" ] +MarkOn.dependencies = [ + "os://scripts/mde/simplemde.min.js", + "os://scripts/mde/simplemde.min.css" +] this.OS.register "MarkOn", MarkOn \ No newline at end of file