开源桌面系统,极简单的mvc设计、数据绑定实现、微事件实现等等。
下载地址:http://git.oschina.net/eternal_rider/sapling/attach_files
设计如下图:
Controller.js
var DtController={ cache:{}, pubevt:function (topic,args,context) { var evtcontext = context || window, subs = evtcontext.DtController.cache[topic], len = subs ? subs.length : 0; while (len--) { subs[len].apply(evtcontext, args || []); } }, subevt:function (topic,callback) { if (!DtController.cache[topic]) { DtController.cache[topic] = []; } DtController.cache[topic].push(callback); return [topic, callback]; }, unsubevt:function (handle,callback) { var subs = DtController.cache[callback ? handle : handle[0]], callback = callback || handle[1], len = subs ? subs.length : 0; while (len--) { if (subs[len] === callback) { subs.splice(len, 1); } } }, bindmodel:function(model,callback){ Object.observe(model, callback); }, init:function(){ DtController.subevt('/dtname/set',DtModel.setDtname); DtController.subevt('/alias/set',DtModel.setAlias); DtController.subevt('/theme/set',DtModel.setTheme); DtController.subevt('/wallpaper/set',DtModel.setWallpaper); DtController.subevt('/mm/load',DtModel.loadMenu); DtController.subevt('/mm/app/set',DtModel.setApp); DtController.subevt('/app/xy/set',DtModel.setAppXY); DtController.bindmodel(DtModel,function(data){ data.forEach(function(item, i){ switch(item.name){ case 'theme': DtView.setTheme(item.object[item.name]); break; case 'wallpaper': DtView.setWallpaper(item.object[item.name]); break; case 'alias': DtView.setAlias(item.object[item.name]); break; case 'dtname': DtView.setDtname(item.object[item.name]); break; case 'menus': DtView.setMenus(item.object[item.name]); break; case 'apps': DtView.setApps(item.object[item.name].list); break; }; //console.log(change.type, change.name, change.oldValue); DtView.setNotifications('监听到DtModel中' + item.name+',触发'+ item.type+'事件'); }); }); DtController.pubevt('/mm/load',[]); var urlstr=window.location.href, username=urlstr.substr(urlstr.indexOf("?")+1); DtModel.setUserName(username); DtView.init(); } };
var DtModel = { dtname:"", username:"", alias:"", theme:"", wallpaper:{}, menus:{}, apps:{}, storage:window.localStorage, loadMenu:function(){ //初始化开始菜单和邮件菜单 $.getJSON("assets/menu.json", function(data) { if (data) { var mm_tmp = [],app_tmp = []; $(data.menu).each(function(i, item) { mm_tmp.push(item); }); $(data.app).each(function(i, item) { app_tmp.push(item); }); var obj = {mm:mm_tmp,app:app_tmp}; DtModel.menus = obj; DtModel.load(); } }); //初始化桌面布局菜单 }, setDtname:function(name){ DtModel.dtname = name; DtModel.save(); }, setUserName:function(username){ DtModel.username = username; }, setAlias:function(alias){ DtModel.alias = alias; DtModel.save(); }, setTheme:function(theme){ DtModel.theme = theme.colors; DtModel.save(); }, setWallpaper:function(data){ DtModel.wallpaper = data; DtModel.save(); }, setApp:function(id){ var list = $.extend([],DtModel.apps.list || []); $.each(list, function(i,data){ if(data.id == id){ list[i].state = list[i].state ==0?1:0; id = "-1"; return false; } }); if(id != "-1"){ $.each(DtModel.menus.app, function(i,data){ if(data.id == id){ data.state = 1; list.push(data); return false; } }); } DtModel.apps = {list:list}; DtModel.save(); }, setAppXY:function(id,x,y){ $.each(DtModel.apps.list, function(i,data){ if(data.id == id){ DtModel.apps.list[i].x=x,DtModel.apps.list[i].y=y; return false; } }); DtModel.apps = {list:DtModel.apps.list}; DtModel.save(); }, save:function(){ if(DtModel.storage) { var data = {}; data.dtname = DtModel.dtname; data.username = DtModel.username; data.alias = DtModel.alias; data.theme=DtModel.theme; data.wallpaper = DtModel.wallpaper; data.apps=DtModel.apps; DtModel.storage.sapling = JSON.stringify(data); //通过websocket保存到服务器 }else{ alert('不支持LocalStorage'); } }, load:function(){ if(DtModel.storage) { if(DtModel.storage.sapling){ var data = JSON.parse(DtModel.storage.sapling); DtModel.dtname = data.dtname; DtModel.username = data.username; DtModel.alias = data.alias ? data.alias:data.username; DtModel.theme = data.theme; DtModel.wallpaper = data.wallpaper; DtModel.apps = data.apps; }else{ DtModel.alias = DtModel.username; } }else{ alert('不支持LocalStorage'); } } }
var DtView = { "init":function(){ setInterval(function() { var time = new Date(); $("#time").html(time.toLocaleTimeString()); }, 1000); using('calendar',function(){ $('#calendar').calendar({ firstDay:1, width:250, height:250 }); }); using('textbox',function(){ $('#dt_name').textbox({ onClickButton:function(){ var text = $('#dt_name').textbox('getText'); if(text){ DtController.pubevt('/dtname/set',[text]); } } }); $('#alias').textbox({ onClickButton:function(){ var text = $('#alias').textbox('getText'); if(text){ DtController.pubevt('/alias/set',[text]); } } }); }); $("#start").mouseenter(function(e) { e.preventDefault(); $(this).attr("src", "assets/ima/2.jpg"); using("menu", function() { $('#startmm').menu('show', { left : 0, top : 40 }); }); }).mouseleave(function() { $(this).attr("src", "assets/ima/1.jpg"); }); $('#dock').on('click','li',function(ev){ var target = $(this).find('a').attr('href'); if($(target).dialog('options').minimized){ $(target).dialog('open'); }else{ $(target).dialog('minimize'); } ev.preventDefault(); ev.stopPropagation(); }); $('#user').mouseenter(function(ev){ ev.preventDefault(); ev.stopPropagation(); $('#calendar').hide(); $('#user_panel').fadeIn(); }); $('#user_panel').mouseleave(function() { $('#user_panel').fadeOut(); }); $('#time').mouseenter(function(ev){ ev.preventDefault(); ev.stopPropagation(); $('#user_panel').hide(); $('#calendar').fadeIn(); }); $('#calendar').mouseleave(function() { $('#calendar').fadeOut(); }); $('#desktop,#task_bar').on('contextmenu',function(e){ e.preventDefault(); var target=e.target||e.srcElement; var tid = $(target).attr('id'); if(tid == 'desktop'){ using("menu", function() { $('#mm-setup').menu('show', { left: e.pageX, top: e.pageY }); }); }else if(tid == 'task_bar'){ alert('任务栏的右键事件'); }else{ return false; } }); $('#setup_app').on('click','li',function(ev){ DtController.pubevt('/mm/app/set',[$(this).attr('id')]); ev.preventDefault(); ev.stopPropagation(); }); $('#user_blog').on('click',function(ev){ ev.preventDefault(); ev.stopPropagation(); DtView.dialogHandler({ "id":"myblog", "title":"作者博客", "link":"http://my.oschina.net/eternal/blog", "load":"iframe", "icon":"world", "width":1280, "height":685 }); }); $('#user_about').on('click',function(ev){ ev.preventDefault(); ev.stopPropagation(); DtView.dialogHandler({ "id":"aboutme", "title":"关于作者", "link":"apps/about/index.html", "load":"iframe", "icon":"user", "maximized":true }); }); $('#help_user').on('click',function(ev){ ev.preventDefault(); ev.stopPropagation(); DtView.dialogHandler({ "id":"helpme", "title":"随便给点吧", "link":"apps/helpme/index.html", "load":"iframe", "icon":"heart", "maximized":true }); }); $('#wallpapers_mm').on('click',function(ev){ ev.preventDefault(); ev.stopPropagation(); DtView.dialogHandler({ "id":"wallpapers", "title":"桌面壁纸", "link":"apps/wallpaper/index.html", "load":"iframe", "icon":"customization", "width":1280, "height":685 }); }); $('#logout').on('click',function(ev){ ev.preventDefault(); ev.stopPropagation(); if (confirm("确认退出系统吗?")==true){ window.location.href="index.html"; } }); }, setDtname:function(name){ document.title = name; using('textbox',function(){ $('#dt_name').textbox('setText',name); }); }, setAlias:function(name){ $('#user').text(name); using('textbox',function(){ $('#alias').textbox('setText',name); }); }, setApps:function(list){ if(!list){ return false; } $.each(list, function(i,data){ var hasapp = $("#desktop").children("#dt_"+data.id).html(); if(!hasapp){ var app = {id:data.id,width:data.width,height:data.height,title:data.title,x:data.x,y:data.y}; $("#dtapp_tmpl").tmpl(app).appendTo("#desktop"); var $appi = $("<iframe border=0 frameborder=0 style='width:100%;height:100%;' scrolling='yes' src='"+data.link+"'></iframe>"); $("#dt_body_"+data.id).html($appi); using("draggable",function(){ $("#dt_"+data.id).draggable({onStopDrag:function(e){ var x = $(this).offset().left,y = $(this).offset().top-41; DtController.pubevt('/app/xy/set',[data.id,x,y]); }}); }); }else{ if(data.state == 0){ $("#desktop").children("#dt_"+data.id).remove(); } } }); }, setMenus:function(menus){ $("#startmm_tmpl").tmpl(menus.mm).appendTo("#startmm"); using('menu',function(){ $('#startmm').menu({ onClick:function(item){ DtView.menuHandler(item); } }); }); $("#app_tmpl").tmpl(menus.app).appendTo("#setup_app"); }, setTheme:function(theme){ var colors = theme.split(','); $('#task_bar').css('background-color',colors[0]).css('border-bottom','1px solid '+colors[1]); $('body').css('background-color',colors[2]); }, setWallpaper:function(data){ if(data.type == 'rgb'){ $('body').css('background',data.name); }else{ $('body').css('background','#777777 url(assets/ima/'+data.name+')'); } }, setNotifications:function(content){ var $lists = $("#user_panel_news").children(".notifications"); if($lists.length >= 3){ $lists[2].remove(); } $("#notifications_tmpl").tmpl({content:content}).prependTo("#user_panel_news"); }, menuHandler:function(item) { var menumodel = {}; $.each(DtModel.menus.mm, function(i,data){ if(data.id == item.id){ menumodel = data; return false; } }); if(menumodel.id){ DtView.dialogHandler(menumodel); } }, dialogHandler:function(menumodel) { if ($("#mm_"+menumodel.id).length > 0) { $("#mm_"+menumodel.id).html(""); } else { $("#desktop").append("<div id='mm_"+menumodel.id+"' style='overflow: hidden;'></div>"); } using('dialog', function() { var dlgconfig = { title:' '+menumodel.title, width:menumodel.width, height:menumodel.height, iconCls:'icon-'+menumodel.icon, collapsible:menumodel.collapsible||true, minimizable:menumodel.minimizable||true, maximizable:menumodel.maximizable||true, resizable:menumodel.resizable||true, draggable:menumodel.draggable||true, inline:true, shadow:true, maximized:menumodel.maximized||false, onBeforeClose:function(){ $('#dock_'+menumodel.id).remove(); $(this).parent().next().remove(); $(this).parent().remove(); }, content : "<iframe border=0 frameborder=0 style='width:100%;height:100%;' scrolling='yes' src='"+menumodel.link+"' sandbox=\"allow-scripts allow-same-origin allow-forms\"></iframe>" }; menumodel.load == "ajax"?dlgconfig.href=menumodel.link:""; $("#mm_"+menumodel.id).dialog(dlgconfig); }); $('<li id="dock_'+menumodel.id+'"><a href="#mm_'+menumodel.id+'"><img src="assets/ima/' + menumodel.icon + '_22.png" />'+menumodel.title+'</a></li>').appendTo('#dock'); } };