mirror of
				https://github.com/lxsang/antos-frontend.git
				synced 2025-10-31 10:26:16 +01:00 
			
		
		
		
	add services
This commit is contained in:
		
							
								
								
									
										13
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ coffees= 	src/define.coffee\ | ||||
|         	src/core/handlers/InBrowserHandler.coffee\ | ||||
|         	src/core/gui/gui.coffee\ | ||||
| 			src/core/gui/BaseApplication.coffee\ | ||||
| 			src/core/gui/BaseService.coffee\ | ||||
| 			src/core/gui/BaseEvent.coffee\ | ||||
|         	src/antos.coffee | ||||
|  | ||||
| @@ -39,11 +40,13 @@ antos_themes = 	src/core/gui/themes/antos/font-awesome.css\ | ||||
|  | ||||
|  | ||||
|  | ||||
| packages = NotePad Terminal ActivityMonitor | ||||
| packages = NotePad wTerm ActivityMonitor DummyApp | ||||
| services = PushNotification Spotlight Calendar | ||||
|  | ||||
| main: clean build_coffee build_tag build_theme schemes libs build_packages | ||||
| main: clean build_coffee build_tag build_theme schemes libs build_services build_packages | ||||
| 	- cp src/index.html $(BUILDDIR)/ | ||||
|  | ||||
| lite: build_coffee build_tag build_theme schemes  build_services build_packages | ||||
| #%.js: %.coffee | ||||
| #		coffee --compile $<  | ||||
|  | ||||
| @@ -84,8 +87,14 @@ antos_themes_build: | ||||
| 	cp src/core/gui/themes/antos/wallpaper.jpg $(BUILDDIR)/resources/themes/antos/ | ||||
|  | ||||
|  | ||||
| build_services: | ||||
| 	@echo "=======$(BLUE)Building services=======$(NC)" | ||||
| 	-mkdir -p $(BUILDDIR)/services | ||||
| 	-rm -rf $(BUILDDIR)/services/* | ||||
| 	for f in $(services); do (coffee -cs < "src/services/$$f.coffee" >$(BUILDDIR)/services/"$$f.js");done | ||||
| build_packages: | ||||
| 	- mkdir $(BUILDDIR)/packages | ||||
| 	- for d in $(packages); do ( test -d $(BUILDDIR)/packages/$$d && rm -rf $(BUILDDIR)/packages/$$d/* ); done | ||||
| 	for d in $(packages); do (cd src/packages/$$d; make);done | ||||
| 	for d in $(packages); do ( test -d $(BUILDDIR)/packages/$$d || mkdir -p $(BUILDDIR)/packages/$$d && cp -rf src/packages/$$d/build/* $(BUILDDIR)/packages/$$d/);done | ||||
| 	for d in $(packages); do ( test -d src/packages/$$d/build && rm -r src/packages/$$d/build ); done | ||||
|   | ||||
| @@ -2,6 +2,7 @@ _GUI = self.OS.GUI | ||||
| _API = self.OS.API | ||||
| _APP = self.OS.APP | ||||
| _PM  = self.OS.PM | ||||
| _courrier = self.OS.courrier | ||||
| this.onload = () -> | ||||
|     console.log "Booting the os" | ||||
|     self.OS.boot() | ||||
| @@ -6,6 +6,7 @@ class BaseApplication | ||||
|         @observable = riot.observable() | ||||
|         @pid = 0 | ||||
|         @_api = self.OS.API | ||||
|  | ||||
|     init: -> | ||||
|         me = @ | ||||
|         # first register some base event to the app | ||||
| @@ -25,7 +26,7 @@ class BaseApplication | ||||
|                 when  "#{me.name}-exit" then me.trigger "exit" | ||||
|         #now load the scheme | ||||
|         path = "packages/#{@name}/scheme.html" | ||||
|         _GUI.loadScheme path ,this | ||||
|         _GUI.loadScheme path , @ | ||||
|  | ||||
|     on: (e, f) -> @observable.on e, f | ||||
|  | ||||
| @@ -49,7 +50,7 @@ class BaseApplication | ||||
|         @exit(evt) | ||||
|         if not evt.prevent | ||||
|             @.appmenu.set "items", [] if @.pid == @.appmenu.pid | ||||
|             _PM.kill(@) | ||||
|             _PM.kill @ | ||||
|             ($ @scheme).remove() | ||||
|      | ||||
|     find: (id) -> ($ "[data-id='#{id}']", @scheme)[0] | ||||
| @@ -85,4 +86,5 @@ class BaseApplication | ||||
|         # to handle the exit event | ||||
|         # use e.preventDefault() to | ||||
|         # discard the quit command | ||||
| BaseApplication.type = 1 | ||||
| this.OS.GUI.BaseApplication = BaseApplication | ||||
							
								
								
									
										40
									
								
								src/core/gui/BaseService.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/core/gui/BaseService.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| MAIL = this.OS.courrier | ||||
| _API = this.OS.API | ||||
| _PM = this.OS.PM | ||||
| class BaseService | ||||
|     constructor: (@name) -> | ||||
|         @icon = undefined | ||||
|         @iconclass = "fa-paper-plane-o" | ||||
|         @text = "" | ||||
|         @_api = _API | ||||
|         @timer = undefined | ||||
|         @holder = undefined | ||||
|  | ||||
|     init: ()-> | ||||
|         #implement by user | ||||
|         # event registe, etc | ||||
|         # scheme loader | ||||
|  | ||||
|     attach: (h) -> @holder = h | ||||
|     update: () -> @holder.update() if @holder | ||||
|     on: (e, f) -> MAIL.on e, f | ||||
|     trigger: (e, d) -> MAIL.trigger e, d | ||||
|     watch: ( t, f) -> | ||||
|         me = @ | ||||
|         func = () -> | ||||
|             f() | ||||
|             me.timer = setTimeout (() -> func()), t | ||||
|         func() | ||||
|     quit: ()-> | ||||
|         console.log "clean timer" if @timer | ||||
|         clearTimeout @timer if @timer | ||||
|         @cleanup() | ||||
|         _PM.kill @ | ||||
|     main: () -> | ||||
|     show: () -> | ||||
|     awake: () -> | ||||
|         #implement by user to tart the service | ||||
|     cleanup:() -> | ||||
|         #implemeted by user | ||||
| BaseService.type = 2 | ||||
| this.OS.GUI.BaseService = BaseService | ||||
| @@ -13,8 +13,8 @@ self.OS.GUI = | ||||
|             ($ "#desktop").append scheme | ||||
|             riot.mount ($ scheme), { observable: app.observable } | ||||
|             app.scheme = scheme[0] | ||||
|             app.show() | ||||
|             app.main() | ||||
|             app.show() | ||||
|         , (f) -> | ||||
|             alert "cannot load scheme" | ||||
|  | ||||
| @@ -23,6 +23,17 @@ self.OS.GUI = | ||||
|         $ "head link#ostheme" | ||||
|             .attr "href", path | ||||
|  | ||||
|     pushService: (srv) -> | ||||
|         return _PM.createProcess srv, _APP[srv] if _APP[srv] | ||||
|         path = "services/#{srv}.js" | ||||
|         $.getScript path | ||||
|             .done (e, s) -> | ||||
|                 _PM.createProcess srv, _APP[srv] | ||||
|             .fail (e, s) -> | ||||
|                 _courrier.trigger "fail", | ||||
|                     { m: "Cannot read service script: #{srv} ", | ||||
|                     e: e, s: s } | ||||
|      | ||||
|     launch: (app) -> | ||||
|         if not _APP[app] | ||||
|             # first load it | ||||
| @@ -42,10 +53,16 @@ self.OS.GUI = | ||||
|                                 _PM.createProcess app, _APP[app] | ||||
|                                 console.log "Fist time loading " + app | ||||
|                             , (e, s) -> | ||||
|                                 _courrier.trigger "fail", | ||||
|                                     { m: "Cannot read application metadata: #{app} ", | ||||
|                                     e: e, s: s } | ||||
|                                 alert "cannot read application, meta-data" | ||||
|                 .fail (e,s) -> | ||||
|                     #BUG report here | ||||
|                     console.log "bug report" | ||||
|                     _courrier.trigger "fail", | ||||
|                         { m: "Cannot load application script: #{app}",  | ||||
|                         e: e, s:s }  | ||||
|                     console.log "bug report", e, s, path | ||||
|         else | ||||
|             # now launch it | ||||
|             if _APP[app] | ||||
| @@ -71,39 +88,36 @@ self.OS.GUI = | ||||
|     undock: (app) -> | ||||
|         ($ "#sysdock").get(0).removeapp app | ||||
|  | ||||
|     attachservice: (srv) -> | ||||
|         ($ "#syspanel")[0].attachservice srv | ||||
|         srv.init() | ||||
|     detachservice: (srv) -> | ||||
|         ($ "#syspanel")[0].detachservice srv | ||||
|     bindContextMenu: (event) -> | ||||
|         handler  = (e) -> | ||||
|             if e.contextmenuHandler | ||||
|                 e.contextmenuHandler event, ($ "#contextmenu")[0] | ||||
|             else | ||||
|                 #console.log ($ "#workspace").get(0), ($ e).parent().get(0) | ||||
|                 p = $(e).parent().get(0) | ||||
|                 handler p if p isnt ($ "#workspace").get(0) | ||||
|         handler event.target | ||||
|         event.preventDefault() | ||||
|     initDM: -> | ||||
|         _API.resource "schemes/dm.html", (x) -> | ||||
|             return null unless x | ||||
|             scheme =  $.parseHTML x | ||||
|             ($ "#wrapper").append scheme | ||||
|             ($ "#desktop").on "click", (e)-> | ||||
|                 return if e.target isnt ($ "#desktop").get(0) | ||||
|             # context menu | ||||
|             riot.mount ($ "#contextmenu") | ||||
|             ($ "#workspace").contextmenu (e) -> _GUI.bindContextMenu e | ||||
|             #desktop | ||||
|             desktop = ($ "#desktop") | ||||
|             desktop.on "click", (e) -> | ||||
|                 return if e.target isnt desktop.get(0) | ||||
|                 ($ "#sysdock").get(0).set "selectedApp", null | ||||
|              | ||||
|             osmenu = {child:[ | ||||
|                 {text:"",iconclass:"fa fa-eercast", child:[ | ||||
|                     {text:"About"}, | ||||
|                     {text:"System Preferences", iconclass:"fa fa-commenting"}, | ||||
|                     {text:"Applications",child:[ | ||||
|                             {text:"Terminal",type:"app"}, | ||||
|                             {text:"NotePad",type:"app", icon:"packages/NotePad/icon.png"}, | ||||
|                             {text:"ActivityMonitor",type:"app"} | ||||
|                         ]}, | ||||
|                     {text:"Logout"} | ||||
|                     ]} | ||||
|                 ],onmenuselect: (item)->  | ||||
|                     switch item.data.type | ||||
|                         when "app" then _GUI.launch item.data.text | ||||
|             } | ||||
|             appmenu = {child:[]} | ||||
|             systray = {child:[ | ||||
|                 {text:"Sun 22:57 6 August 2017"}, | ||||
|                 {text:"",iconclass:"fa fa-search"}, | ||||
|                 {text:"",iconclass:"fa fa-commenting"} | ||||
|                  | ||||
|                 ],onmenuselect: (item)->  | ||||
|                     console.log item | ||||
|             } | ||||
|  | ||||
|             riot.mount ($ "#syspanel", $ "#wrapper"),{osmenu:osmenu,appmenu:appmenu,systray:systray} | ||||
|             desktop.get(0).contextmenuHandler = (e, m) -> | ||||
|                 console.log "context menu handler for desktop" | ||||
|             # system menu | ||||
|             riot.mount ($ "#syspanel", $ "#wrapper") | ||||
|             riot.mount ($ "#sysdock", $ "#wrapper"), { items: [] } | ||||
| @@ -6,3 +6,4 @@ | ||||
|     <div id = "desktop"> | ||||
|     </div> | ||||
| </div> | ||||
| <afx-menu id="contextmenu" context="true" style="display:none;"></afx-menu> | ||||
|   | ||||
| @@ -39,10 +39,6 @@ | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         minimize() | ||||
|         { | ||||
|             this.root.observable.trigger("hide") | ||||
| @@ -108,6 +104,7 @@ | ||||
|                 else  | ||||
|                     self.root.observable.trigger("focus") | ||||
|             }) | ||||
|             self.root.observable.trigger("loaded", self.root) | ||||
|         }) | ||||
|         var enable_dragging = function() | ||||
|         { | ||||
|   | ||||
| @@ -51,10 +51,9 @@ | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         this.on("mount", function(){ | ||||
|             window.OS.courrier.trigger("sysdockloaded") | ||||
|         }) | ||||
|          | ||||
|     </script> | ||||
| </afx-apps-dock> | ||||
| @@ -23,10 +23,6 @@ | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         this._onbtclick = function(e) | ||||
|         { | ||||
|             if(typeof opts.onbtclick == 'string') | ||||
|   | ||||
| @@ -82,7 +82,6 @@ | ||||
|                     data:event.item} | ||||
|             if(opts.onlistselect) | ||||
|                 opts.onlistselect(data) | ||||
|             console.log(data) | ||||
|             if(self.selidx != -1) | ||||
|                 self.rows[self.selidx].selected =false | ||||
|             self.selidx = event.item.i | ||||
|   | ||||
| @@ -4,23 +4,6 @@ | ||||
|     </div> | ||||
|     <script> | ||||
|         var self = this | ||||
|         self.root.set = function(k,v) | ||||
|         { | ||||
|             if(k == "*") | ||||
|                 for(var i in v) | ||||
|                     self[i] = v[i] | ||||
|             else | ||||
|                 self[k] = v | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.get = function(k) | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         this.on('mount', function(){ | ||||
|             $(self.refs.container) | ||||
|                 .css("display","flex") | ||||
| @@ -44,6 +27,7 @@ | ||||
|             var auto_height = [] | ||||
|             var csize, ocheight = 0, avaiheight; | ||||
|             avaiheight = $(self.root).parent().height() | ||||
|             avaiwidth = $(self.root).parent().width() | ||||
|             $(self.refs.container).css("height",avaiheight + "px") | ||||
|             $(self.refs.container) | ||||
|                 .children() | ||||
| @@ -69,6 +53,8 @@ | ||||
|             { | ||||
|                 $(v).css("height", csize + "px") | ||||
|             }) | ||||
|             self.root.observable.trigger("hboxchange", | ||||
|                 {id:$(self.root).attr("data-id"), w:avaiwidth, h:csize}) | ||||
|         } | ||||
|     </script> | ||||
| </afx-hbox> | ||||
| @@ -28,10 +28,6 @@ | ||||
|                 self[k] = v | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.get = function(k) | ||||
|         { | ||||
|             if(k == "selected") | ||||
| @@ -69,7 +65,6 @@ | ||||
|         { | ||||
|             var desktoph = $("#desktop").height() | ||||
|             var off = $(self.root).offset().top + $(self.refs.mlist).height() | ||||
|             console.log(desktoph,off) | ||||
|             if( off > desktoph ) | ||||
|                 $(self.refs.mlist) | ||||
|                     .css("top","-" +  $(self.refs.mlist).outerHeight() + "px") | ||||
|   | ||||
| @@ -1,23 +1,30 @@ | ||||
| <afx-menu > | ||||
|     <ul> | ||||
|     <ul class={context: opts.context == "true"}> | ||||
|         <li class="afx-corner-fix"></li> | ||||
|         <li each={ items } class = {afx_submenu:child != null, fix_padding:icon}> | ||||
|         <li ref = "container" each={ item,i in items } class = {afx_submenu:item.child != null, fix_padding:item.icon} no-reorder> | ||||
|             <a href="#" onclick = {parent.onselect}> | ||||
|                 <i if={iconclass} class = {iconclass} ></i> | ||||
|                 <i if={icon} class="icon-style" style = { "background: url("+icon+");background-size: 100% 100%;background-repeat: no-repeat;" }></i> | ||||
|                 { text } | ||||
|                 <i if={item.iconclass} class = {item.iconclass} ></i> | ||||
|                 <i if={item.icon} class="icon-style" style = { "background: url("+item.icon+");background-size: 100% 100%;background-repeat: no-repeat;" }></i> | ||||
|                 { item.text } | ||||
|             </a> | ||||
|              | ||||
|             <afx-menu if={child != null} child={child} onmenuselect={onmenuselect} observable = {parent.root.observable} rootid = {parent.rid}></afx-menu> | ||||
|             <afx-menu  if={item.child != null} child={item.child}  observable = {parent.root.observable} rootid = {parent.rid}></afx-menu> | ||||
|         </li> | ||||
|          <li class="afx-corner-fix"></li> | ||||
|     </ul> | ||||
|     <script> | ||||
|         this.items = opts.child | ||||
|         this.items = opts.child || [] | ||||
|         var isRoot | ||||
|         if(opts.rootid) | ||||
|         { | ||||
|             this.rid = opts.rootid | ||||
|             isRoot = false | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             this.rid = $(this.root).attr("data-id") | ||||
|             isRoot = true | ||||
|         } | ||||
|         var self = this | ||||
|         this.onmenuselect = opts.onmenuselect | ||||
|         self.root.set = function(k,v) | ||||
| @@ -29,14 +36,45 @@ | ||||
|                 self[k] = v | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.get = function(k) | ||||
|         self.root.push = function(e,u) | ||||
|         { | ||||
|             return self[k] | ||||
|             self.items.push(e) | ||||
|             if(u) | ||||
|                 self.update() | ||||
|         } | ||||
|         self.root.unshift = function(e,u) | ||||
|         { | ||||
|             self.items.unshift(e) | ||||
|             if(u) | ||||
|                 self.update() | ||||
|         } | ||||
|         self.root.remove = function(e,u) | ||||
|         { | ||||
|             var i = self.items.indexOf(e) | ||||
|             if(i >= 0) | ||||
|                 self.items.splice(i, 1) | ||||
|             if(u) | ||||
|                 self.update() | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.get = function(k) | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.show = function(e) | ||||
|         { | ||||
|             //only for menucontext | ||||
|             if(opts.context != "true") return; | ||||
|             $(self.root) | ||||
|                 .css("top", e.clientY - 15 + "px") | ||||
|                 .css("left",e.clientX  -5 +  "px") | ||||
|                 .show() | ||||
|             $(document).on("click",mnhide) | ||||
|         } | ||||
|  | ||||
|         if(opts.observable) | ||||
|         { | ||||
|             this.root.observable = opts.observable | ||||
| @@ -47,23 +85,61 @@ | ||||
|             this.root.observable.on('menuselect',function(data){ | ||||
|                 //console.log("From root",self.root) | ||||
|                 if(self.onmenuselect) | ||||
|                 { | ||||
|                     self.onmenuselect(data) | ||||
|  | ||||
|                 if(opts.context == "true") | ||||
|                     $(self.root).hide() | ||||
|                 else if(!data.root && self.refs.container) | ||||
|                 { | ||||
|                     var arr = self.refs.container.length?self.refs.container:[self.refs.container] | ||||
|                     for( var i in arr) | ||||
|                         $("afx-menu",arr[i]).first().hide() | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|          | ||||
|         var mnhide = function(event) | ||||
|         { | ||||
|             if(opts.context == "true") | ||||
|             { | ||||
|                 if(!$(event.target).closest(self.root).length) { | ||||
|                     $(self.root).hide() | ||||
|                     $(document).unbind("click",mnhide) | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|             if(!$(event.target).closest(self.refs.container).length && self.refs.container) { | ||||
|                 var arr = self.refs.container.length?self.refs.container:[self.refs.container] | ||||
|                 for( var i in arr) | ||||
|                     $("afx-menu",arr[i]).first().hide() | ||||
|                 $(document).unbind("click",mnhide) | ||||
|             }  | ||||
|             else  | ||||
|             { | ||||
|                 if(self.refs.container && self.refs.container.length) | ||||
|                     for(var i in self.refs.container) | ||||
|                         if(!$(event.target).closest(self.refs.container[i]).length) { | ||||
|                             $("afx-menu",self.refs.container[i]).first().hide() | ||||
|                         }  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         onselect(event) | ||||
|         { | ||||
|             var data = {id:self.rid, data:event.item} | ||||
|            /*if(self.onmenuselect) | ||||
|             { | ||||
|                 self.onmenuselect(data) | ||||
|             } else*/ | ||||
|             var data = {id:self.rid, data:event.item.item, root:isRoot} | ||||
|             this.root.observable.trigger('menuselect',data) | ||||
|             event.preventDefault() | ||||
|             $(document).unbind("click",mnhide) | ||||
|             if(opts.context == "true") return | ||||
|             if(isRoot && self.refs.container) | ||||
|             { | ||||
|                 if(self.refs.container.length) | ||||
|                     $("afx-menu",self.refs.container[event.item.i]).first().show() | ||||
|                 else  | ||||
|                     $("afx-menu",self.refs.container).first().show() | ||||
|                 $(document).on("click",mnhide) | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     </script> | ||||
|   | ||||
							
								
								
									
										3
									
								
								src/core/gui/tags/afx-service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/core/gui/tags/afx-service.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| <afx-service> | ||||
|     <yield/> | ||||
| </afx-service> | ||||
| @@ -1,16 +1,42 @@ | ||||
| <afx-sys-panel> | ||||
|     <div> | ||||
|         <afx-menu data-id = "os_menu" ref = "aOsmenu" child={osmenu.child} onmenuselect = {osmenu.onmenuselect} class="afx-panel-os-menu"></afx-menu> | ||||
|         <afx-menu data-id = "appmenu" ref = "aAppmenu" child={appmenu.child} onmenuselect = {appmenu.onmenuselect} class = "afx-panel-os-app"></afx-menu> | ||||
|         <afx-menu data-id = "appmenu" ref = "aAppmenu" child={appmenu.child}  class = "afx-panel-os-app"></afx-menu> | ||||
|         <afx-menu data-id = "sys_tray" ref = "aTray" child={systray.child} onmenuselect = {systray.onmenuselect} class = "afx-panel-os-stray"></afx-menu> | ||||
|     </div> | ||||
|      | ||||
|     <script> | ||||
|         this.osmenu = opts.osmenu | ||||
|         this.appmenu = opts.appmenu | ||||
|         this.systray = opts.systray | ||||
|         var self = this | ||||
|         this.osmenu = {child:[ | ||||
|                 {text:"",iconclass:"fa fa-eercast", child:[ | ||||
|                     {text:"About"}, | ||||
|                     {text:"System Preferences", iconclass:"fa fa-commenting"}, | ||||
|                     {text:"Applications",child:[ | ||||
|                             {text:"wTerm",type:"app"}, | ||||
|                             {text:"NotePad",type:"app", iconclass:"fa fa-commenting"}, | ||||
|                             {text:"ActivityMonitor",type:"app"}, | ||||
|                             {text:"DummyApp",type:"app"} | ||||
|                         ]}, | ||||
|                     {text:"Logout"} | ||||
|                     ]} | ||||
|                 ],onmenuselect: function(item) | ||||
|                 { | ||||
|                     if(item.data.type == "app") | ||||
|                         window.OS.GUI.launch(item.data.text) | ||||
|                 } | ||||
|             } | ||||
|         this.appmenu = { child: [] } | ||||
|         this.systray = { child: [], onmenuselect: function(item){item.data.awake()}} | ||||
|  | ||||
|         var self = this | ||||
|         self.root.attachservice = function(s) | ||||
|         { | ||||
|             s.attach(self.refs.aTray) | ||||
|             self.refs.aTray.root.unshift(s,true) | ||||
|         } | ||||
|         self.root.detachservice = function(s) | ||||
|         { | ||||
|             self.refs.aTray.root.remove(s, true) | ||||
|         } | ||||
|         self.root.set = function(k,v) | ||||
|         { | ||||
|             if(k == "*") | ||||
| @@ -24,15 +50,12 @@ | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         this.on('mount', function() { | ||||
|             //console.log(self.refs.aOsmenu.root) | ||||
|             $(self.refs.aOsmenu.root).css("z-index",1000000) | ||||
|             $(self.refs.aAppmenu.root).css("z-index",1000000) | ||||
|             $(self.refs.aTray.root).css("z-index",1000000) | ||||
|             window.OS.courrier.trigger("syspanelloaded") | ||||
|         }) | ||||
|     </script> | ||||
| </afx-sys-panel> | ||||
| @@ -49,10 +49,6 @@ | ||||
|                 self[k] = v | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.get = function(k) | ||||
|         { | ||||
|             return self[k] | ||||
|   | ||||
| @@ -4,23 +4,6 @@ | ||||
|     </div> | ||||
|     <script> | ||||
|         var self = this | ||||
|         self.root.set = function(k,v) | ||||
|         { | ||||
|             if(k == "*") | ||||
|                 for(var i in v) | ||||
|                     self[i] = v[i] | ||||
|             else | ||||
|                 self[k] = v | ||||
|             self.update() | ||||
|         } | ||||
|         self.root.get = function(k) | ||||
|         { | ||||
|             return self[k] | ||||
|         } | ||||
|         self.root.update = function() | ||||
|         { | ||||
|             self.update() | ||||
|         } | ||||
|         this.on('mount', function(){ | ||||
|             $(self.refs.container) | ||||
|                 .css("display","flex") | ||||
| @@ -36,7 +19,6 @@ | ||||
|                     }) | ||||
|                 } | ||||
|         }) | ||||
|  | ||||
|         var calibrate_size = function() | ||||
|         { | ||||
|             var auto_width = [] | ||||
| @@ -51,6 +33,7 @@ | ||||
|                     this.observable = self.root.observable | ||||
|                     $(this) | ||||
|                         .css("flex-grow","1") | ||||
|                         //.css("height",avaiheight + "px") | ||||
|                     var dw = $(this).attr("data-width") | ||||
|                     if(dw) | ||||
|                     { | ||||
| @@ -67,6 +50,8 @@ | ||||
|             { | ||||
|                 $(v).css("width", csize + "px") | ||||
|             }) | ||||
|             self.root.observable.trigger("vboxchange", | ||||
|                 {id:$(self.root).attr("data-id"), w:csize, h:avaiheight}) | ||||
|         } | ||||
|     </script> | ||||
| </afx-vbox> | ||||
| @@ -66,7 +66,7 @@ afx-menu  afx-menu li{ | ||||
|     float:none; | ||||
|     min-width: 150px; | ||||
| } | ||||
| afx-menu  afx-menu afx-menu{ | ||||
| afx-menu  afx-menu afx-menu, afx-menu  ul.context afx-menu{ | ||||
|     top:-4px; | ||||
|     left: 100%; | ||||
| } | ||||
| @@ -78,7 +78,7 @@ afx-menu li:hover > a { | ||||
|     color: white; | ||||
| } | ||||
|  | ||||
| afx-menu li:hover > afx-menu | ||||
| afx-menu afx-menu   li:hover > afx-menu, ul.context  li:hover > afx-menu | ||||
| { | ||||
|     display: block; | ||||
| } | ||||
| @@ -92,7 +92,7 @@ afx-menu li.afx-corner-fix:hover{ | ||||
|     background-color: transparent; | ||||
| } | ||||
|  | ||||
| afx-menu afx-menu .afx_submenu:before { | ||||
| afx-menu afx-menu .afx_submenu:before, afx-menu  ul.context  .afx_submenu:before{ | ||||
|     content: "\f054";  | ||||
|     font-family: "FontAwesome"; | ||||
|     font-size: 10px; | ||||
| @@ -101,3 +101,19 @@ afx-menu afx-menu .afx_submenu:before { | ||||
|     position:absolute; | ||||
|     top:25%; | ||||
|  } | ||||
|  | ||||
|  afx-menu ul.context{ | ||||
|     position: absolute; | ||||
|     z-index: 1000000; | ||||
|     padding: 0; | ||||
|     border:1px solid #a6a6a6; | ||||
|     border-radius: 5px; | ||||
|     border-top-left-radius: 0px; | ||||
|     /*box-shadow: 2px 2px 2px #cbcbcb;*/ | ||||
|     box-shadow: 1px 1px 1px #9f9F9F; | ||||
|     background-color: #e7e7e7; | ||||
|  } | ||||
|  afx-menu  ul.context li{ | ||||
|      clear:float; | ||||
|      min-width: 150px; | ||||
|  } | ||||
| @@ -15,7 +15,7 @@ self.OS.PM = | ||||
|             _PM.pidalloc++ | ||||
|             obj.pid = _PM.pidalloc | ||||
|             _PM.processes[app].push obj | ||||
|             _GUI.dock obj,cls.meta | ||||
|             if cls.type is 1 then _GUI.dock obj, cls.meta else _GUI.attachservice obj | ||||
|     appByPid:(pid)-> | ||||
|         app = undefined | ||||
|         find = (l) -> | ||||
| @@ -30,6 +30,7 @@ self.OS.PM = | ||||
|  | ||||
|         i = _PM.processes[app.name].indexOf app | ||||
|         if i >= 0 | ||||
|             _GUI.undock _PM.processes[app.name][i] | ||||
|             p = _PM.processes[app.name][i] | ||||
|             if _APP[app.name].type == 1 then _GUI.undock p else _GUI.detachservice p | ||||
|             delete _PM.processes[app.name][i] | ||||
|             _PM.processes[app.name].splice i, 1 | ||||
|   | ||||
| @@ -5,6 +5,7 @@ self.OS or= | ||||
|     GUI: new Object() | ||||
|     APP: new Object() | ||||
|     PM: new Object() | ||||
|     courrier: riot.observable() | ||||
|     register: (name,x)-> | ||||
|         # load the metadata first | ||||
|         _APP[name] = x | ||||
| @@ -15,4 +16,8 @@ self.OS or= | ||||
|         _GUI = self.OS.GUI | ||||
|         _GUI.loadTheme "antos" | ||||
|         _GUI.initDM() | ||||
|         #_GUI.loadScheme "resources/schemes/test.html",null  | ||||
|         _courrier.on "syspanelloaded", () -> | ||||
|             _GUI.pushService "PushNotification" | ||||
|             _GUI.pushService "Spotlight" | ||||
|             _GUI.pushService "Calendar" | ||||
|          | ||||
| @@ -14,7 +14,7 @@ class ActivityMonitor extends this.OS.GUI.BaseApplication | ||||
|             app = _PM.appByPid item[0].value | ||||
|             app.quit() if app | ||||
|  | ||||
|         header = [{width:50,value:"Pid"},{value:"Name"},{width:100,value:"Alive (ms)"}] | ||||
|         header = [{width:50,value:"Pid"},{value:"Name"}, {value:"Type", width:75},{width:70,value:"Alive (ms)"}] | ||||
|         @gdata =  | ||||
|             processes:{} | ||||
|             alive:[] | ||||
| @@ -29,11 +29,14 @@ class ActivityMonitor extends this.OS.GUI.BaseApplication | ||||
|         $.each _PM.processes, (i,d)-> | ||||
|             $.each d , (j,a)-> | ||||
|                 if me.gdata.processes[a.pid] #update it | ||||
|                     me.gdata.processes[a.pid][2].value = now - a.birth | ||||
|                     me.gdata.processes[a.pid][3].value = now - a.birth | ||||
|                 else #add it | ||||
|                     me.gdata.processes[a.pid] = [ | ||||
|                         {value:a.pid}, | ||||
|                         {icon:_APP[a.name].meta.icon,iconclass:_APP[a.name].meta.iconclass,value:a.name}, | ||||
|                         {icon:if _APP[a.name].type == 1 then _APP[a.name].meta.icon else a.icon, | ||||
|                         iconclass:if _APP[a.name].type == 1 then _APP[a.name].meta.iconclass else a.iconclass, | ||||
|                         value:a.name}, | ||||
|                         {value: if _APP[a.name].type == 1 then "Application" else "Service"} | ||||
|                         {value: now - a.birth} | ||||
|                     ] | ||||
|                 me.gdata.alive.push a.pid | ||||
|   | ||||
| @@ -13,7 +13,7 @@ NC=\033[0m | ||||
| main: title clean js css copy | ||||
| 
 | ||||
| title: | ||||
| 	@echo "$(BLUE)======= Package Terminal =======$(NC)" | ||||
| 	@echo "$(BLUE)======= Package DummyApp =======$(NC)" | ||||
| 
 | ||||
| coffee: | ||||
| 	- mkdir build | ||||
| @@ -1,6 +1,6 @@ | ||||
| class Terminal extends this.OS.GUI.BaseApplication | ||||
| class DummyApp extends this.OS.GUI.BaseApplication | ||||
|     constructor: () -> | ||||
|         super "Terminal" | ||||
|         super "DummyApp" | ||||
|     main: () -> | ||||
|         self = @ | ||||
|         @on "btclick", (e)-> | ||||
| @@ -19,7 +19,7 @@ class Terminal extends this.OS.GUI.BaseApplication | ||||
|         tdata = { | ||||
|             name: 'My Tree', | ||||
|             nodes: [ | ||||
|                 { name: 'hello', icon:'packages/NotePad/icon.png'}, | ||||
|                 { name: 'hello', icon:'fa fa-car'}, | ||||
|                 { name: 'wat' }, | ||||
|                 { | ||||
|                     name: 'child folder', | ||||
| @@ -76,5 +76,12 @@ class Terminal extends this.OS.GUI.BaseApplication | ||||
|         list.set "onlistselect", (e)-> | ||||
|             console.log e | ||||
| 
 | ||||
| Terminal.singleton = false | ||||
| this.OS.register "Terminal",Terminal | ||||
|         @scheme.set "apptitle", "AntOS feature showcase" | ||||
| 
 | ||||
|         @scheme.contextmenuHandler = (e, m) -> | ||||
|             mdata = [ { text: " Child 1" }, { text: "child2", child: [{text: "sub child", child:[{text:"sub sub child"}] }]}] | ||||
|             m.set "items", mdata | ||||
|             m.show(e) | ||||
| 
 | ||||
| DummyApp.singleton = false | ||||
| this.OS.register "DummyApp",DummyApp | ||||
							
								
								
									
										12
									
								
								src/packages/DummyApp/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/packages/DummyApp/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| { | ||||
|     "app":"DummyApp", | ||||
|     "name":"DummyApp", | ||||
|     "description":"App for test", | ||||
|     "author":{ | ||||
|         "name": "Xuan Sang LE", | ||||
|         "email": "xsang.le@gmail.com" | ||||
|     }, | ||||
|     "category":"System", | ||||
|     "iconclass":"fa fa-user-circle-o", | ||||
|     "mimes":["*"] | ||||
| } | ||||
| @@ -14,7 +14,7 @@ class NotePad extends this.OS.GUI.BaseApplication | ||||
|             enableBasicAutocompletion: true, | ||||
|             enableSnippets: true, | ||||
|             enableLiveAutocompletion: true, | ||||
|             fontSize: "10pt" | ||||
|             fontSize: "9pt" | ||||
|         } | ||||
|         @.editor.completers.push {getCompletions:(editor, session, pos, prefix, callback)->} | ||||
|         @.editor.getSession().setUseWrapMode true | ||||
| @@ -34,8 +34,7 @@ class NotePad extends this.OS.GUI.BaseApplication | ||||
|             l = me.editor.session.getLength() | ||||
|             $(stat).html "Row #{c.row}, col #{c.column}, lines: #{l}" | ||||
|         stup(0) | ||||
|         @.editor.getSession().selection.on "changeCursor", (e)-> | ||||
|             stup(e) | ||||
|         @.editor.getSession().selection.on "changeCursor", (e)->stup(e) | ||||
|  | ||||
|         @on "resize", ()-> me.editor.resize() | ||||
|         @on "focus", ()->me.editor.focus() | ||||
|   | ||||
| @@ -1,9 +1,16 @@ | ||||
|  | ||||
| afx-app-window[data-id="notepad"] afx-list-view[data-id="modelist"] { | ||||
|     margin: 2px; | ||||
|     margin-right: 5px; | ||||
| } | ||||
|  | ||||
| afx-app-window[data-id="notepad"] afx-list-view[data-id="modelist"] div.list-container{ | ||||
|     z-index: 10; | ||||
| } | ||||
|  | ||||
| afx-app-window[data-id="notepad"] span[data-id="editorstat"]{ | ||||
|     padding:5px; | ||||
|     display: inline-block; | ||||
| } | ||||
| afx-app-window[data-id="notepad"] afx-vbox[data-id="bottom-vbox"]{ | ||||
|     background-color: #dfdfdf; | ||||
| } | ||||
| @@ -15,7 +15,7 @@ | ||||
|              | ||||
|         </afx-hbox--> | ||||
|         <div data-id="datarea"></div> | ||||
|         <afx-vbox data-height="30"> | ||||
|         <afx-vbox data-height="30" data-id="bottom-vbox"> | ||||
|             <div ><span data-id = "editorstat"></span></div> | ||||
|              <afx-list-view data-width="170" data-id = "modelist" dropdown = "true" width="150"></afx-list-view> | ||||
|               | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/packages/wTerm/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/packages/wTerm/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| coffee_files = main.coffee | ||||
|  | ||||
| jsfiles = xterm.js  | ||||
|  | ||||
| cssfiles = xterm.css main.css  | ||||
|  | ||||
| copyfiles = scheme.html package.json | ||||
|  | ||||
|  | ||||
| BLUE=\033[1;34m | ||||
| NC=\033[0m | ||||
|  | ||||
| main: title clean js css copy | ||||
|  | ||||
| title: | ||||
| 	@echo "$(BLUE)======= Package wTerm =======$(NC)" | ||||
| 	- rm -rf build/* | ||||
|  | ||||
| coffee: | ||||
| 	- mkdir build | ||||
| 	for f in $(coffee_files); do (coffee -cs < $$f >build/"$$f.js");done | ||||
| 	for f in build/*.coffee.js; do (cat "$${f}"; echo) >> build/main.js; done | ||||
| 	- rm build/*.coffee.js | ||||
|  | ||||
| js: coffee | ||||
| 	for f in $(jsfiles); do (cat "$${f}"; echo) >> build/main.js; done | ||||
|  | ||||
| css: | ||||
| 	for f in $(cssfiles); do (cat "$${f}"; echo) >> build/main.css; done | ||||
|  | ||||
| copy: | ||||
| 	cp -rf $(copyfiles) build/ | ||||
| clean: | ||||
| 	- rm -rf build/* | ||||
							
								
								
									
										71
									
								
								src/packages/wTerm/main.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/packages/wTerm/main.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| class wTerm extends this.OS.GUI.BaseApplication | ||||
|     constructor: () -> | ||||
|         super "wTerm" | ||||
|      | ||||
|     main: () -> | ||||
|         me = @ | ||||
|         @scheme.set "apptitle", "Terminal" | ||||
|         @mterm = @find "myterm" | ||||
|         @term = new Terminal { cursorBlink: true } | ||||
|         @term.on "key", (d, e) -> | ||||
|             me.socket.send "i#{d}" if me.socket | ||||
|         @term.on 'title', () -> console.log "title change" | ||||
|         @term.open @mterm | ||||
|         @socket = null | ||||
|         #@on "resize", () -> me.resizeContent() | ||||
|         @on "focus", () -> me.term.focus() | ||||
|         # handle the paste event | ||||
|         area = ($ ".xterm-helper-textarea", @mterm)[0] | ||||
|         area.onpaste = (e) -> | ||||
|             #ifreturn false unless @socket | ||||
|             pastedText = undefined | ||||
|             if window.clipboardData and window.clipboardData.getData  #IE | ||||
|                 pastedText = window.clipboardData.getData 'Text' | ||||
|             else if e.clipboardData and e.clipboardData.getData | ||||
|                 pastedText = e.clipboardData.getData 'text/plain' | ||||
|             return false unless pastedText | ||||
|             # send by chunk, to ease the handle on server side | ||||
|             len = pastedText.length | ||||
|             chunklen = len / 1000 + if (len % 1000 == 0) then 0 else 1 | ||||
|             for i in [0..(len - 1)] | ||||
|                 end = if (i + 1) * 1000 > len then len else (i + 1) * 1000 | ||||
|                 me.term.write pastedText.substring i * 1000, end | ||||
|         #self.socket.send("i"+ substr.replace(/\n/g,"\r\n")) | ||||
|         @openSession() | ||||
|         @on "vboxchange", (e) -> me.resizeContent e.w, e.h | ||||
|  | ||||
|     resizeContent: (w, h) -> | ||||
|         ex = @term.rowContainer.firstElementChild | ||||
|         oldhtml = ($ ex).html() | ||||
|         ($ ex).css "display", "inline" | ||||
|         ($ ex).html "W" | ||||
|         ncol = parseInt (w / ($ ex).width()) | ||||
|         nrow = parseInt (h / ($ ex).height()) | ||||
|         ($ ex).css "display", "" | ||||
|         ($ ex).html oldhtml | ||||
|         @term.resize ncol, nrow | ||||
|         return if not @socket or (@socket.readyState isnt @socket.OPEN) | ||||
|         #initialGeometry = @.term.proposeGeometry() | ||||
|         #cols = initialGeometry.cols | ||||
|         #rows = initialGeometry.rows | ||||
|         #console.log "send", "s#{ncol}:#{nrow}" | ||||
|         @socket.send "s#{ncol}:#{nrow}" | ||||
|  | ||||
|     openSession: () -> | ||||
|         me = @ | ||||
|         @term.clear() | ||||
|         @term.focus() | ||||
|         @socket = new WebSocket "ws://" + window.location.host + "/wterm" | ||||
|         @socket.onopen = () -> | ||||
|             #el.style.display = "none" | ||||
|             me.resizeContent (($ me.mterm).width()) ,  (($ me.mterm).height()) | ||||
|             me.term.focus() | ||||
|  | ||||
|         @socket.onmessage =  (e) -> me.term.write e.data if me.term and e.data | ||||
|         @socket.onclose = () -> | ||||
|             me.socket = null | ||||
|             console.log "socket closed" | ||||
|             #el.style.display = "block" | ||||
|     exit: (e)-> | ||||
|         @socket.close() if @socket | ||||
| this.OS.register "wTerm",wTerm | ||||
							
								
								
									
										0
									
								
								src/packages/wTerm/main.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/packages/wTerm/main.css
									
									
									
									
									
										Normal file
									
								
							| @@ -1,5 +1,5 @@ | ||||
| { | ||||
|     "app":"Terminal", | ||||
|     "app":"wTerm", | ||||
|     "name":"Unix terminal like", | ||||
|     "description":"Access Unix terminal from web", | ||||
|     "author":{ | ||||
							
								
								
									
										5
									
								
								src/packages/wTerm/scheme.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/packages/wTerm/scheme.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <afx-app-window apptitle="Preview" width="600" height="400"> | ||||
|     <afx-vbox data-id = "mybox"> | ||||
|         <div data-id="myterm" ></div> | ||||
|     </afx-vbox> | ||||
| </afx-app-window> | ||||
							
								
								
									
										2261
									
								
								src/packages/wTerm/xterm.css
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										2261
									
								
								src/packages/wTerm/xterm.css
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										5132
									
								
								src/packages/wTerm/xterm.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5132
									
								
								src/packages/wTerm/xterm.js
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										23
									
								
								src/services/Calendar.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/services/Calendar.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| class Calendar extends this.OS.GUI.BaseService | ||||
|     constructor: () -> | ||||
|         super "Calendar" | ||||
|         #@iconclass = "fa fa-commenting" | ||||
|         @text = "" | ||||
|         @iconclass = "fa fa-calendar" | ||||
|     init: -> | ||||
|         #update time each second | ||||
|         me = @ | ||||
|         @watch 1000, () -> | ||||
|             now = new Date | ||||
|             me.text = "#{now.getDate()}/#{(now.getMonth()+1)}/#{now.getFullYear()} " + | ||||
|                     "#{now.getHours()}:#{now.getMinutes()}:#{now.getSeconds()}" | ||||
|             me.update() | ||||
|         | ||||
|     awake: -> | ||||
|         console.log @name,@pid | ||||
|         # do nothing | ||||
|     cleanup: -> | ||||
|         console.log "cleanup for quit" | ||||
|         # do nothing | ||||
|  | ||||
| this.OS.register "Calendar",Calendar | ||||
							
								
								
									
										13
									
								
								src/services/PushNotification.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/services/PushNotification.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| class PushNotification extends this.OS.GUI.BaseService | ||||
|     constructor: () -> | ||||
|         super "PushNotification" | ||||
|         @iconclass = "fa fa-commenting" | ||||
|  | ||||
|     init: -> | ||||
|         # do nothing | ||||
|     awake: -> | ||||
|         console.log @name,@pid | ||||
|     cleanup: -> | ||||
|         # do nothing | ||||
|  | ||||
| this.OS.register "PushNotification",PushNotification | ||||
							
								
								
									
										13
									
								
								src/services/Spotlight.coffee
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/services/Spotlight.coffee
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| class Spotlight extends this.OS.GUI.BaseService | ||||
|     constructor: () -> | ||||
|         super "Spotlight" | ||||
|         @iconclass = "fa fa-search" | ||||
|  | ||||
|     init: -> | ||||
|         # do nothing | ||||
|     awake: -> | ||||
|         console.log @name,@pid | ||||
|     cleanup: -> | ||||
|         # do nothing | ||||
|  | ||||
| this.OS.register "Spotlight",Spotlight | ||||
		Reference in New Issue
	
	Block a user