[转载]How to Build Applications Based On AndAR

mikel阅读(956)

[转载]HowToBuildApplicationsBasedOnAndAR – andar – This page describes how someone can build augmented reality applications using AndAR. – AndAR – Android Augmented Reality – Google Project Hosting.

AndAR项目是Andorid的(Augmented Reality)增强现实开源项目

项目地址:http://code.google.com/p/andar/

License

The whole project is released under the GNU General Public License. This means it can be used in any project that is itself released under the GPL. If you would like to create a commercial application based on AndAR please contact ARToolworks.

Eclipse sample project Howto

This section is about the Eclipse sample project, alternatively you may check out the source code from the SVN repository.

AndAR Architecture

AndAR is an Augmented Reality Framework for Android. It not only offers a pure Java API but is also object oriented. The figure above shows a simplied class diagram of an application that makes use of AndAR.

Every Android application consists of one or more Activities. An Activity is a visual user interface, targeted to a single purpose. Only one may be active at a time. In order to write an Augmented Reality application, one has to extend the abstract class AndARActivity. This class already handles everything Augmented Reality related, like opening the camera, detecting the markers and displaying the video stream. The application would run already, by just doing that. However it would not detect any markers.

In order to do so, you have to register ARObjects to an instance of ARToolkit. This instance can be retrieved from the AndARActivity. The ARObject class itself is abstract. This means, it has to be extended, too. It expects the file name of a pattern file in it’s constructor. This file must be located in the assets folder of the Eclipse project.

Pattern files can be created by a tool called mk_patt, as described here. They are used to distinguish different markers. In order to draw a custom object, the method draw has to be overridden. Before this method is invoked a transformation matrix will already have been applied. This means the object will be alligned to the marker, without any further steps. This method will not be invoked, if the marker belonging to this object is not visible.

The class ARRenderer is reponsible for everything OpenGL related. If you want to mix augmented with non augmented 3D objects you may provide a class implementing the OpenGLRenderer interface. There are three methods defined by this interface. initGL being called only once, when the OpenGL surface is initialized. Whereas setupEnv is called once before the augmented objects are drawn. It can be used to issue OpenGL commands that shall effect all ARObjects, like initializing the lighting. In the draw method you may draw any non augmented 3D objects. It will be called once for every frame. Specifying such the described renderer is optional. The AndARActivity furthermore offers a method that allows the application to take screenshots.

[转载]实战 HTML5 WebSocket 聊天室.net实现

mikel阅读(1424)

[转载]实战 HTML5 WebSocket 聊天室.net实现 – 大月&小年 – 博客园.

WebSocket protocol 是HTML5一种新的协议(protocol)。目前紧测试了三个浏览器支持(Chrome,Firefox4,Safari)

客户端:

var ws = new WebSocket(con);

//与服务器握手成功
ws.onopen = onOpen;
//接收到服务器消息
ws.onmessage = onMessage;
//断开连接消息
ws.onclose = onClose;
//通读错误

ws.onerror = onError;

进入代码正题服务端

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace DotNetWebSocket.Engine
{
    //广播事件
    public delegate void BroadcastEvent(MessageEntity me);

    public class WebSocketServer:IDisposable
    {
        private Socket serverListener;
        //回调,用于消息传给上层应用
        ICallback callback = null;
        //广播事件
        public BroadcastEvent BroadcastMessage=null;
        //客户端连接列表
        List<ClientSocketInstance> listConnection = new List<ClientSocketInstance>();


        public WebSocketServer(ICallback callback)
        {
            this.callback = callback;
        }

        /// <summary>
        /// 启动等待连接
        /// </summary>
        public void StartConnection()
        {
            serverListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);  // Start the socket

            string[] ip = WebSocketProtocol.GetInstance.ServerId.Split('.');
            IPAddress localIp = new IPAddress(new byte[] { Convert.ToByte(ip[0]), Convert.ToByte(ip[1]), Convert.ToByte(ip[2]),Convert.ToByte(ip[3]) });
            serverListener.Bind(new IPEndPoint(localIp, WebSocketProtocol.GetInstance.ServerPort));
            
            serverListener.Listen(WebSocketProtocol.GetInstance.ConnectionsCount);
            while (true)
            {
                //等待客户端请求
               Socket sc = serverListener.Accept();
               if (sc != null)
                {
                    Thread.Sleep(100);
                    ClientSocketInstance ci = new ClientSocketInstance();
                    ci.ClientSocket = sc;
                    //初始化三个事件
                    ci.NewUserConnection += new ClientSocketEvent(Ci_NewUserConnection);
                    ci.ReceiveData += new ClientSocketEvent(Ci_ReceiveData);
                    ci.DisConnection += new ClientSocketEvent(Ci_DisConnection);
                    //开始与客户端握手[握手成功,即可通讯了]
                    ci.ClientSocket.BeginReceive(ci.receivedDataBuffer, 0, ci.receivedDataBuffer.Length, 0, new AsyncCallback(ci.StartHandshake), ci.ClientSocket.Available);
                    listConnection.Add(ci);

                }
                               
            }
           
        }      

     
        /// <summary>
        /// 断开服务端Socket
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="me"></param>
        private void Ci_DisConnection(object sender, MessageEntity me)
        {
            callback.DisConnection(sender as ClientSocketInstance, me);
        }
        
        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="me"></param>
        private void Ci_ReceiveData(object sender, MessageEntity me)
        {
            callback.Read(sender as ClientSocketInstance, me);
        }
        
        /// <summary>
        /// 握手成功手的连接
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="me"></param>
        private void Ci_NewUserConnection(object sender, MessageEntity me)
        {
            ClientSocketInstance ci=sender as ClientSocketInstance;
            BroadcastMessage += new BroadcastEvent(ci.SendMessage);
            callback.NewUserConnectionJoin(ci, me);

        }

        #region IDisposable 成员

        public void Dispose()
        {
            serverListener = null;
        }

        #endregion
    }
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using Newtonsoft.Json;
using DotNetWebSocket.Engine.Message;

namespace DotNetWebSocket.Engine
{
    
    public delegate void ClientSocketEvent(object sender,MessageEntity me);

    public class ClientSocketInstance
    {
        private byte[] ServerKey1;
        private byte[] ServerKey2;

        public string name;
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public Socket ClientSocket;

        public byte[] receivedDataBuffer;


        public event ClientSocketEvent NewUserConnection;
        public event ClientSocketEvent ReceiveData;
        public event ClientSocketEvent DisConnection;

        public ClientSocketInstance()
        {
            receivedDataBuffer = new byte[WebSocketProtocol.GetInstance.MaxBufferSize];
            ServerKey1 = new byte[4];
            ServerKey2 = new byte[4];
           
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="result"></param>
        private void Read(IAsyncResult result)
        {
            if (!ClientSocket.Connected) return;
            try
            {
                // Web Socket protocol: 0x00开头,0xFF结尾
                System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
                int startIndex = 0;
                int endIndex = 0;

                //查找起启位置
                while (receivedDataBuffer[startIndex] == 0x00) startIndex++;
                // 查找结束位置
                endIndex = startIndex + 1;
                while (receivedDataBuffer[endIndex] != 0xff && endIndex != WebSocketProtocol.GetInstance.MaxBufferSize - 1) endIndex++;
                if (endIndex == WebSocketProtocol.GetInstance.MaxBufferSize - 1) endIndex = WebSocketProtocol.GetInstance.MaxBufferSize;

               
                string messageReceived = decoder.GetString(receivedDataBuffer, startIndex, endIndex - startIndex);

                MessageEntity me = JsonConvert.DeserializeObject(messageReceived, typeof(MessageEntity)) as MessageEntity;
                if (!string.IsNullOrEmpty(this.Name))
                {
                    ReceiveData(this, me);
                }
                else if (me.MessageId.ToLower() == "login")
                {
                    if (NewUserConnection != null)
                    {

                        this.Name = (Newtonsoft.Json.JsonConvert.DeserializeObject(me.MessageContent,typeof(ChartMessage)) as ChartMessage).Message;
                        NewUserConnection(this, me);
                    }
                }             

               /* MessageEntity me=new MessageEntity();
                me.MessageContent = messageReceived;
                ReceiveData(this, me);*/
                Array.Clear(receivedDataBuffer, 0, receivedDataBuffer.Length);
                ClientSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, new AsyncCallback(Read), null);
            }
            catch(Exception ex)
            {
                DisConnection(this,null);
            }
        }
        
        /// <summary>
        /// 发送与客户端握手信息
        /// </summary>
        /// <param name="status"></param>
        public void  StartHandshake(IAsyncResult status)
        {
           int ClientHandshakeLength = (int) status.AsyncState;          
            byte[] last8Bytes = new byte[8];
            Array.Copy(receivedDataBuffer, ClientHandshakeLength - 8, last8Bytes, 0, 8); 
            ASCIIEncoding decoder = new System.Text.ASCIIEncoding();
            string ClientHandshake = decoder.GetString(receivedDataBuffer, 0, ClientHandshakeLength - 8);
            string[] ClientHandshakeLines = ClientHandshake.Split(new string[] { Environment.NewLine }, System.StringSplitOptions.RemoveEmptyEntries);
                      
            
            /*请求中的Sec-WebSocket-Key1中所有的数字连在一起
             * 然后除以空格的个数,得到结果1。
             * 然后从Key2同样的得到结果2,
             * 这两个结果取整后切断为32位整数,
             * 然后转成大头的网络顺序(Big-Endian),
             * 这两个结果和请求中最后的8个字节拼在一起,
             * 然后计算MD5。
              这个MD5的16字节结果就是服务器的反馈key*/

            //计算16位的服务端Key
            foreach (string Line in ClientHandshakeLines)
            {
              
                if (Line.Contains("Sec-WebSocket-Key1:"))
                    BuildServerSecKey(1, Line.Substring(Line.IndexOf(":") + 2));
                if (Line.Contains("Sec-WebSocket-Key2:"))
                    BuildServerSecKey(2, Line.Substring(Line.IndexOf(":") + 2));
            }

            //握手头信息
            byte[] HandshakeText = Encoding.ASCII.GetBytes(WebSocketProtocol.GetInstance.ServerHandshake);

            byte[] serverHandshakeResponse = new byte[HandshakeText.Length + 16];
            byte[] serverKey = BuildFullServerSecKey(last8Bytes);
            Array.Copy(HandshakeText, serverHandshakeResponse, HandshakeText.Length);
            Array.Copy(serverKey, 0, serverHandshakeResponse, HandshakeText.Length, 16);
            ClientSocket.BeginSend(serverHandshakeResponse, 0, HandshakeText.Length + 16, 0, HandshakeSuccess, null);          
        }

        /// <summary>
        /// 根据客户端握手Key生成客户端响应给客户端的安全Key
        /// </summary>
        /// <param name="keyNum"></param>
        /// <param name="clientKey"></param>
        private void BuildServerSecKey(int keyNum, string clientKey)
        {
            string partialServerKey = "";
            byte[] currentKey;
            int spacesNum = 0;
            char[] keyChars = clientKey.ToCharArray();
            //根据客户端Key获取得其中的空格数及其中的数字
            foreach (char currentChar in keyChars)
            {
                if (char.IsDigit(currentChar)) partialServerKey += currentChar;
                if (char.IsWhiteSpace(currentChar)) spacesNum++;
            }
            try
            {
                //用获取的数字除于空格数,再转成大头网络数据
                currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey) / spacesNum));
                if (BitConverter.IsLittleEndian) Array.Reverse(currentKey); 

                if (keyNum == 1) ServerKey1 = currentKey;
                else ServerKey2 = currentKey;
            }
            catch
            {
                if (ServerKey1 != null) Array.Clear(ServerKey1, 0, ServerKey1.Length);
                if (ServerKey2 != null) Array.Clear(ServerKey2, 0, ServerKey2.Length);
            }
        }


        /// <summary>
        ///生成完整的16位安全Key[将Key1和Key2加在一起再加客户端握手信息的手八位] MD5后返回
        /// </summary>
        /// <returns></returns>
        private byte[] BuildFullServerSecKey(byte[] last8Bytes)
        {
            byte[] concatenatedKeys = new byte[16];
            Array.Copy(ServerKey1, 0, concatenatedKeys, 0, 4);
            Array.Copy(ServerKey2, 0, concatenatedKeys, 4, 4);
            Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8);

            // MD5 Hash
            System.Security.Cryptography.MD5 MD5Service = System.Security.Cryptography.MD5.Create();
            return MD5Service.ComputeHash(concatenatedKeys);
        }

        /// <summary>
        /// 握手成功,此时客户端与服务端建立接连,可进行通讯
        /// </summary>
        /// <param name="result"></param>
        private void HandshakeSuccess(IAsyncResult result)
        {
            ClientSocket.EndSend(result);
           
            ClientSocket.BeginReceive(receivedDataBuffer, 0, receivedDataBuffer.Length, 0, new AsyncCallback(Read), null);
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="me"></param>
        public void SendMessage(MessageEntity me)
        {
            ClientSocket.Send(new byte[] {0x00});
          
            ClientSocket.Send(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(me)));
            ClientSocket.Send(new byte[] { 0xff });
        }

      
    }

[转载]ASP.NET MVC3缓存之二:页面缓存中的局部动态

mikel阅读(1065)

[转载]MVC3缓存之二:页面缓存中的局部动态 – 好记性不如烂键盘 – 博客园.

上一篇我们讨论了MVC中使用页面缓存的一些方法,而其中由于页面缓存的粒度太粗,不能对页面进行局部的缓存,或者说,如果我们想在页面缓存的同时对局部进行动态输出该怎么办?下面我们看下这类问题的处理。

MVC中有一个Post-cache substitution的东西,可以对缓存的内容进行替换。

使用Post-Cache Substitution

  • 定义一个返回需要显示的动态内容string的方法。
  • 调用HttpResponse.WriteSubstitution()方法即可。

示例,我们在Model层中定义一个随机返回新闻的方法。

using System;
using System.Collections.Generic;
using System.Web;

namespace MvcApplication1.Models
{
public class News
{
public static string RenderNews(HttpContext context)
{
var news = new List
{
"Gas prices go up!",
"Life discovered on Mars!",
"Moon disappears!"
};

var rnd = new Random();
return news[rnd.Next(news.Count)];
}
}

}

然后在页面中需要动态显示内容的地方调用。

&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %&gt;
&lt;%@ Import Namespace="MvcApplication1.Models" %&gt;



Index
<div>&lt;% Response.WriteSubstitution(News.RenderNews); %&gt;
<hr />
The content of this page is output cached.
&lt;%= DateTime.Now %&gt;</div>

如在上一篇文章中说明的那样,给Controller加上缓存属性。

using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : Controller
{
[OutputCache(Duration=60, VaryByParam="none")]
public ActionResult Index()
{
return View();
}
}

}
[/chsarp]
可以发现,程序对整个页面进行了缓存60s的处理,但调用WriteSubstitution方法的地方还是进行了随机动态显示内容。
对Post-Cache Substitution的封装

将静态显示广告Banner的方法封装在AdHelper中。

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Helpers
{
public static class AdHelper
{
public static void RenderBanner(this HtmlHelper helper)
{
var context = helper.ViewContext.HttpContext;
context.Response.WriteSubstitution(RenderBannerInternal);
}

private static string RenderBannerInternal(HttpContext context)
{
var ads = new List
{
"/ads/banner1.gif",
"/ads/banner2.gif",
"/ads/banner3.gif"
};

var rnd = new Random();
var ad = ads[rnd.Next(ads.Count)];
return String.Format("<img src="{0}" alt="" />", ad);
}
}

}

这样在页面中只要进行这样的调用,记得需要在头部导入命名空间。

&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %&gt;
&lt;%@ Import Namespace="MvcApplication1.Models" %&gt;
&lt;%@ Import Namespace="MvcApplication1.Helpers" %&gt;



Index
<div>&lt;% Response.WriteSubstitution(News.RenderNews); %&gt;
<hr />
&lt;% Html.RenderBanner(); %&gt;
<hr />
The content of this page is output cached.
&lt;%= DateTime.Now %&gt;</div>

使用这样的方法可以使得内部逻辑对外呈现出更好的封装。

[转载]百度地图API国内首款团购网站的地图插件

mikel阅读(809)

[转载]【百度地图API】——国内首款团购网站的地图插件 – 酸奶小妹 – 博客园.

摘要:

本文介绍了一款应用在团购网站上的地图插件,适用于目前非常流行的团购网站。使用这款地图插件,无需任何编程技术,你就把商家的位置轻松地标注在地图上。

前台地址

http://map.baidu.com/fwmap/upload/r/map/fwmap/tuangou/user.html

后台地址

http://map.baidu.com/fwmap/upload/r/map/fwmap/tuangou/admin.html

———————————————————————————————————

团购网站地图插件使用说明:

前台用户浏览页面:

1、首先要在页面中放一个DOM容器

例如:

<div id="BAIDUMAPCONTENT"></div>

2、在页面的最下方(后前)加上下面的脚本

<script type="text/javascript">

//需要展示地图的元素ID

var BAIDUMAPCONTENT = "BAIDUMAPCONTENT";

//地图上需要展示的点信息

var BAIDUPOINTS = [{"name":"麻辣诱惑(十里堡店)新开张中新开张中","address":"北京市朝阳区十里堡甲3号都会国际A座2(华堂商场西)","tel":"(010)65579989","point":"116.510133,39.923652","citycode":131}];

</script>

<script type="text/javascript" src="http://map.baidu.com/fwmap/upload/r/map/fwmap/tuangou/js/user.js"></script>


3、这时候浏览页面就会在页面上创建出一个小地图来

详细说明:

步骤2中,两个变量必须提供且名字不能改,值可以改;

var BAIDUMAPCONTENT = "BAIDUMAPCONTENT";

这个变量的值为1中DOM容器的ID,可以更改,但要跟DOM容器的ID保持一致,并且保证DOM容器存在

var BAIDUPOINTS = [{"name":"麻辣诱惑(十里堡店)新开张中新开张中","address":"北京市朝阳区十里堡甲3号都会国际A座2(华堂商场西)","tel":"(010)65579989","point":"116.510133,39.923652","citycode":131}];

这个变量的值的类型为数组,里面存放的是点对象(目前最多只能存放10个点对象)

点对象:

{
"name":"麻辣诱惑(十里堡店)新开张中新开张中", // 名称 必填

"address":"北京市朝阳区十里堡甲3号都会国际A座2(华堂商场西)", //地址 必填

"tel":"(010)65579989", //电话 选填

"point":"116.510133,39.923652", //坐标 必填

"citycode":131 //城市代码 必填

}

点对象中包括 name、address、tel、point、citycode 5个属性,其中 name 、address、point、citycode 4个属性是必填的,tel是选填的,如果4个必填属性中有一个值为空或不存在,那这个点,将不会标注在地图上显示

示例地址:

http://map.baidu.com/fwmap/upload/r/map/fwmap/tuangou/user.html

——————————————————————————————————————————-

后台管理页面:

1、首先要在页面上放一个textarea元素和一个DOM容器

<textarea style="width:280px;" id="BAIDUMAPINFO">[{"name":"麻辣诱惑(十里堡店)新开张中新开张中","address":"北京市朝阳区十里堡甲3号都会国际A座2(华堂商场西)","tel":"(010)65579989"}]</textarea>;



<span id="BDMAPBUTTON"></span>


2、在页面的最下方(后前)加上下面的脚本

<script type="text/javascript">

    var BAIDUMAPINFO = 'BAIDUMAPINFO';//存放点信息的textarea的ID

    var BDMAPBUTTON = 'BDMAPBUTTON';//存放地图标注按钮的容器ID

</script>

<script type="text/javascript" src="http://map.baidu.com/fwmap/upload/r/map/fwmap/tuangou/js/admin.js"></script>

3、这时候浏览页面就会在页面上创建出一个 小按钮,点击按钮会弹出个地图

详细说明:

步骤2中,两个变量必须提供且名字不能改,值可以改;

var BAIDUMAPINFO = 'BAIDUMAPINFO';//存放点信息的textarea的ID

这个变量的值为1中textarea容器的ID,修改时需同时修改;

var BDMAPBUTTON = 'BDMAPBUTTON';//存放地图标注按钮的容器ID

这个变量的值为1中DOM容器的ID,修改时需同时修改;

textarea中存放的是一个数组,数组中存放的是点对象(目前最多只能存放10个点对象)

点对象:

{

"name":"麻辣诱惑(十里堡店)新开张中新开张中", // 名称 必填

"address":"北京市朝阳区十里堡甲3号都会国际A座2(华堂商场西)", //地址 必填

"tel":"(010)65579989", //电话 选填

"point":"116.510133,39.923652", //坐标 选填 没有时会根据 地址(address)自动生成

"citycode":131 //城市代码 选填 没有时会根据 地址(address)自动生成

}

点对象中包括 name、address、tel、point、citycode 5个属性,其中 name 、address 2个属性是必填的,tel、point、citycode是选填的,如果2个必填属性中有一个值为空或不存在,那这个点,将不会标注在地图上显示和编辑,point、citycode 2个属性如果不存在时,会根据地址(address)自动生成出来

point、citycode 2个属性生成说明:如果这两个属性存在时,则直接读取使用,如果不存在会自动生成出来,当查看完地图,关闭地图时会将textarea的值用重新生成后的内容替换掉

如何修改点坐标:

打开地图后,地图会自动将符合条件的点显示在地图上,点击左侧的 详情 会将该条信息在地图上对应的点的气泡打开,用鼠标左键按住气泡下方的 Marker (有A、B、C、D、E、F、G、H、I、J显示的元素)移动,就可以改变改点的坐标,移动完成后,点击左侧列表中改点对应的信息框中的 确定 按钮,该点坐标就修改成功了,如果想取消移动,点击 对应的 取消 按钮 就可以了

示例地址:

http://map.baidu.com/fwmap/upload/r/map/fwmap/tuangou/admin.html

———————————————————————————————————-

前台页面的图示:

[转载]HTML5标准学习 - 文档结构

mikel阅读(849)

[转载]HTML5标准学习 – 文档结构 – Novice Doodle from Gray Zhang – 博客园.

说起HTML的结构,很多人都能说得头头是道,一般来说答案可能是这样的:

一个DOCTYPE,一个html,里面有head和body元素。

这当然不能说是不正确的,但是如果问到一个最小的HTML源文件必须有哪一些东西的话,恐怕很少有人能正确地做出回答。

先来回答一下这个问题,一个最简的HTML5源码文件需要的内容如下:

<!DOCTYPE html>

是的,就这样,一个字符不多,一个字符不少,除了大小写可任意变化外,其他的任何内容都是不能变动的。

那么究竟是怎么样的规则,导致一个最简的源码文件必须有doctype声明呢?根据标准,一个HTML文档有如下内容组成(严格按照顺序):

  1. 一个BOM标记,且这个BOM标记必须为U+FEFF。
  2. 0-n个空格或注释。
  3. DOCTYPE声明。
  4. 0-n个空格或注释。
  5. 一个HTML元素。
  6. 0-n个空格或注释。

这里存在着一些和HTML4的不同,一个HTML4的最简源码文件是这样的:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                      "http://www.w3.org/TR/html4/loose.dtd">
<title>这里是标题</title>

两者的区别是显而易见的:

  • HTML5把DOCTYPE修改为更简单的<!DOCTYPE html>,这个已经众所周知了。
  • 在HTML4中多了一个<title>标签。

这里的重点就是<title>标签了,关于这个标签,在HTML4.01标准中是这么说的:

Every HTML document must have a TITLE element in the HEAD section.

也即是说,HTML4要求<title>标签是必须存在的。

而在HTML5的标准中,又是这么说的:

There must be no more than one title element per document.

HTML5中只设定了<title>标签数量的上限,却没有指明下限,也就是说,没有<title>的文档已经被视为一个合法的文档了。

对于DOCTYPE,HTML4中设定了6种DOCTYPE,HTML5中将DOCTYPE分为3种,这个在以后的章节中再具体说明。

再回过来看一下文档组成,除去0-n个空格或注释这样并没有多大意义的元素之外,组成的列表中还说明有一个HTML元素,但是最简的源码中却没有这东西。这是因为在HTML的规范中,一直存在“隐式标签”这样的概念,关于隐式标签,大致可以这么解释:

一部分元素,当满足特定的前提条件时,其开始标签或结束标签可以在源码中省略。在这种情况下,被省略的标签称为“隐式标签”。

需要注意的是,此处的省略指的是在源码中省略,而在最终成型的DOM树中,这个标签是存在的,因此才称为隐式标签。因此上面最简的源码结构,在生成DOM树后,其真正的结构是这样的:

<!DOCTYPE html>
<html>
    <head></head>
    <body></body>
</html>

最后,再总结一下XHTML中的一些规范:

  • 因为是XML,所以为了表示这是一个HTML文档,必须有一个命名空间,其值为http://www.w3.org/1999/xhtml
  • 因为是XML,所以MIME type不能是text/html了,text/xmlapplication/xmlapplication/xml+html都是比较好的选择。
  • 因为是XML,必须有根元素,根元素为<html>,即<html>的开始和结束标签不能省略了。
  • 因为是XML,所有元素只要有了开始标签,就不能没有结束标签,或者自闭合。
  • 因为是XML,所有元素都得严格遵守大小写,元素名称必须为小写。

因为是XML,文档变得严格了很多,也因为是XML,其可读性和规范性提高了不少。但最终,我们始终要在HTML的宽容性和XML的规范性之间找到最佳的平衡点,一味地追求极端始终是一个错误。

[转载]HTML5标准学习 - 文档结构

mikel阅读(868)

[转载]HTML5标准学习 – 文档结构 – Novice Doodle from Gray Zhang – 博客园.

说起HTML的结构,很多人都能说得头头是道,一般来说答案可能是这样的:

一个DOCTYPE,一个html,里面有head和body元素。

这当然不能说是不正确的,但是如果问到一个最小的HTML源文件必须有哪一些东西的话,恐怕很少有人能正确地做出回答。

先来回答一下这个问题,一个最简的HTML5源码文件需要的内容如下:

<!DOCTYPE html>

是的,就这样,一个字符不多,一个字符不少,除了大小写可任意变化外,其他的任何内容都是不能变动的。

那么究竟是怎么样的规则,导致一个最简的源码文件必须有doctype声明呢?根据标准,一个HTML文档有如下内容组成(严格按照顺序):

  1. 一个BOM标记,且这个BOM标记必须为U+FEFF。
  2. 0-n个空格或注释。
  3. DOCTYPE声明。
  4. 0-n个空格或注释。
  5. 一个HTML元素。
  6. 0-n个空格或注释。

这里存在着一些和HTML4的不同,一个HTML4的最简源码文件是这样的:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                      "http://www.w3.org/TR/html4/loose.dtd">
<title>这里是标题</title>

两者的区别是显而易见的:

  • HTML5把DOCTYPE修改为更简单的<!DOCTYPE html>,这个已经众所周知了。
  • 在HTML4中多了一个<title>标签。

这里的重点就是<title>标签了,关于这个标签,在HTML4.01标准中是这么说的:

Every HTML document must have a TITLE element in the HEAD section.

也即是说,HTML4要求<title>标签是必须存在的。

而在HTML5的标准中,又是这么说的:

There must be no more than one title element per document.

HTML5中只设定了<title>标签数量的上限,却没有指明下限,也就是说,没有<title>的文档已经被视为一个合法的文档了。

对于DOCTYPE,HTML4中设定了6种DOCTYPE,HTML5中将DOCTYPE分为3种,这个在以后的章节中再具体说明。

再回过来看一下文档组成,除去0-n个空格或注释这样并没有多大意义的元素之外,组成的列表中还说明有一个HTML元素,但是最简的源码中却没有这东西。这是因为在HTML的规范中,一直存在“隐式标签”这样的概念,关于隐式标签,大致可以这么解释:

一部分元素,当满足特定的前提条件时,其开始标签或结束标签可以在源码中省略。在这种情况下,被省略的标签称为“隐式标签”。

需要注意的是,此处的省略指的是在源码中省略,而在最终成型的DOM树中,这个标签是存在的,因此才称为隐式标签。因此上面最简的源码结构,在生成DOM树后,其真正的结构是这样的:

<!DOCTYPE html>
<html>
    <head></head>
    <body></body>
</html>

最后,再总结一下XHTML中的一些规范:

  • 因为是XML,所以为了表示这是一个HTML文档,必须有一个命名空间,其值为http://www.w3.org/1999/xhtml
  • 因为是XML,所以MIME type不能是text/html了,text/xmlapplication/xmlapplication/xml+html都是比较好的选择。
  • 因为是XML,必须有根元素,根元素为<html>,即<html>的开始和结束标签不能省略了。
  • 因为是XML,所有元素只要有了开始标签,就不能没有结束标签,或者自闭合。
  • 因为是XML,所有元素都得严格遵守大小写,元素名称必须为小写。

因为是XML,文档变得严格了很多,也因为是XML,其可读性和规范性提高了不少。但最终,我们始终要在HTML的宽容性和XML的规范性之间找到最佳的平衡点,一味地追求极端始终是一个错误。

[转载]基于jQuery的可用于选项卡及幻灯的切换插件

mikel阅读(1017)

[转载]基于jQuery的可用于选项卡及幻灯的切换插件 – Mr.Think的博客@MrThink.net… – 博客园.

基于jQuery的可用于选项卡及幻灯的切换插件

最近公司项目页面中用到选项卡与幻灯比较多,特地写了个集选项卡、幻灯片与播放控制于一体的插件,同页面可多次使用。思路就不说了,记得以前写过一个自动切换的幻灯插件:http://mrthink.net/jq-plugin-ifadeslide/,思路有部分是类似的。当然,本文中插件源码中也有注释~ 插件核心代码:点此查看样例

$.fn.WIT_SetTab=function(iSet){
	/*
	 * @Mr.Think
	 * Nav: 导航钩子;
	 * Field:切换区域
	 * K:初始化索引;
	 * CurCls:高亮样式;
	 * Auto:是否自动切换;
	 * AutoTime:自动切换时间;
	 * OutTime:淡入时间;
	 * InTime:淡出时间;
	 * CrossTime:鼠标无意识划过时间
	 * Ajax:是否开启ajax
	 * AjaxFun:开启ajax后执行的函数
	 */
	iSet=$.extend({Nav:null,Field:null,K:0,CurCls:'cur',Auto:false,AutoTime:4000,OutTime:100,InTime:150,CrossTime:60},iSet||{});
	var acrossFun=null,hasCls=false,autoSlide=null;
	//切换函数
	function changeFun(n){
		iSet.Field.filter(':visible').fadeOut(iSet.OutTime, function(){
			iSet.Field.eq(n).fadeIn(iSet.InTime).siblings().hide();
		});
		iSet.Nav.eq(n).addClass(iSet.CurCls).siblings().removeClass(iSet.CurCls);
	}
	//初始高亮第一个
	changeFun(iSet.K);
	//鼠标事件
	iSet.Nav.hover(function(){
		iSet.K=iSet.Nav.index(this);
		if(iSet.Auto){
			clearInterval(autoSlide);
		}
		hasCls = $(this).hasClass(iSet.CurCls);
		//避免无意识划过时触发
		acrossFun=setTimeout(function(){
			//避免当前高亮时划入再次触发
			if(!hasCls){
				changeFun(iSet.K);
			}
		},iSet.CrossTime);
	},function(){
		clearTimeout(acrossFun);
		//ajax调用
		if(iSet.Ajax){
			iSet.AjaxFun();
		}
		if(iSet.Auto){
			//自动切换
			autoSlide = setInterval(function(){
	            iSet.K++;
	            changeFun(iSet.K);
	            if (iSet.K == iSet.Field.size()) {
	                changeFun(0);
					iSet.K=0;
	            }
	        }, iSet.AutoTime)
		}
	}).eq(0).trigger('mouseleave');
}

[转载]在Web程序中将doc,pdf,jpg等文档转化为swf之FlashPaper终极配置

mikel阅读(972)

[转载]在Web程序中将doc,pdf,jpg等文档转化为swf之FlashPaper终极配置 – 互联网软件@产业 – 博客园.

FlashPaper是将其他格式(如txt,doc,pdf,jpg,xls)的文档转化为swf文件的工具;

这是在web应用程序/网站中调用FlashPaper.exe转化文件的详细可重复配置。

在此之前从网上找了很多FlashPaper的配置,几乎都没有提出完整的可重复配置的方法。

该配置是在win server 2003,IIS6多次测试成功的,其他环境是否可行未知。

基本软件
1.    FlashPaper2.2程序目录
2.    安装Flash player  10.2.152.32 (activex和plugin两个安装程序)
3.    初始化:运行FlashPaper2.2程序目录中的“初始化.bat”
4.    如果转pdf须安装Adobe Reader 9.1或9.2
5.    如果转doc须安装Word 2007

配置方法
1.    检查“Print Spooler”进程开启;
2.    检查“Macromedia FlashPaper”打印机安装正确;
3.    通过本地用户和组管理,将Network service用户(须查找该用户)添加到administrators组中, 执行命令iisrset重启iis;
4.    Word必须是2007,2003需要在DCOM中设置权限,并且不一定能转换成功;

如果仍有问题可能需检查:
1.    转换文档读取和保存的文件夹的读写权限;
2.    打印机权限;
3.    Aspnet用户添加到读写权限;
4.    远程打开word等软件的权限;

关于模板
Flashpaper 安装目录下有个Interface文件夹,里边的DefaultViewer2.swf就是模板文件。可反编译DefaultViewer2.swf。生 成新的DefaultViewer.swf,替换原来的。比如去掉某个功能,去掉或替换logo等;

测试方法
1.    首先保证将文件直接拖到FlashPrinter.exe可以正确转化,此时可确定FlashPaper本身的配置已经正确;
2.    .txt,.doc,.pdf要分别测试,一个格式的能转换并不一定其他格式可以,与相应用到的应用程序是否安装及访问权限有关

[转载]改善ERP的用户体验,个性化用户界面(Jquery 提供源码)

mikel阅读(1207)

[转载]改善ERP的用户体验,个性化用户界面(Jquery 提供源码) – 小管 – 博客园.

这篇文章讲述的技术问题并不多,如果你是想了解技术实现,请直接跨过文章下载源码或者看demo

我大胆起这个名字,有点标题党。希望能对一部分人给予帮助。

创作背景:

互联网和企业内部管理系统发展的过程中,基于ajax技术构建的门户是2.0这一时代中最为成功的web应用程序。而igoogle、 pageflakes、dropthings以及后来的n多站点mybaidu、mymsn等等,同时他们把ajax的真正潜能挖掘出来。这些门户站点为 用户提供个性化的界面,使得他们仅仅通过一个站点就能获取整个互联网上自己关心的知识信息和娱乐资讯,同时也为企业信息内容整合带来了帮助。关于这个技术 实现有个标准叫UWA ,sohu继承了该标准好像叫做SWA,  我的理解就是一个框架,开发人员需要使用他们的接口,说到这里我不得不说下一个英雄人物:JQuery. 使得一切变的那么简单。如果这些还不能打动你,我举一个实际情况,不知道你是否遇到过。

比如进销存(大的说ERP)软件:

销售人员:开销售订单,(销售订单-》点击开单-》打开开单据的页面),因为销售订单下面可能还有退回单,不同层次技术的人,对这个菜单的划分也是不一样的。

销售人员还要负责收款,可能还需要开收款单。同样在左边的导航菜单中,找到收款单。

部门经理: 由于权限大了,看到的菜单会多些。为了审批一张单据,他和销售人员的工作量是一样的。

总经理:会看到什么?  权限最大,销售订单、采购单、出库入库单、收款单、付款单等等,可能还有对应退货单。还有大量的报表。

对于这种情况业务应该是差不多的,由于是不同的软件厂商,所以各个角色的操作可能不同。但是如果左边是菜单,右边展示单据信息的这种软件,给人的体验应该是相同的。

一个明显的缺点:

对于使用人员:第一次打开系统,进入眼前的并不是我想看到的内容,而且我想看到得内容 鬼知道在那里。
对于开发人员:划分菜单的权限,麻烦。至少我感觉到是。也许是我的水平太差了。第二需要给使用人员培训,那个报表在哪里,等等。
用户点击的次数增多。我见过的金蝶K3也是如此。

采用后的好处:

一目了然的界面。

很清晰的看到自己需要的数据、自己要做的事情。

方便拓展。(比如外部天气、新闻、地图等等)

为了实现这个技术:购买的书籍

《道不远人-深入解析 ASP.NET 2.0 控件开发》

ASP.NET 3.5 构建Web 2.0 门户站点》

ASP.NET Ajax 与 Silverlight 实践手册》

这几本书籍的解释,请允许我发几句牢骚:)

道不远人-深入解析 Asp.Net 2.0 控件开发,是冲着书中提到WebPart买的。如果你想学习控件开发,这本书籍还是不错的,但是为了项目中使用webpart,介绍的还不够深入。 当时我找 WebPart的书籍,很少几乎没有。这本书中提到了webpart开发。其实webpart这东西,我认为微软做的并不好,供开发人员开发的东西太少。 而且任何操作都要提交到服务器。 我强烈建议你不使用webpart应用到项目中。
Asp.Net 3.5 构建Web 2.0 门户站点: 源于dropthings.com这个站点,是一个开源项目,微软的技术,拖拽的状态改变是使用的WF实现、还用到了Linq to SQL、AjaxToolkit。 很佩服的是这个站点的性能是不错的,书中介绍的很全面。
Asp.Net Ajax 与 Silverlight 实践手册:因为webpart放到了updatapanel里面,所以想了解Updatapanel的工作原理。

分享:

yixiu80.codeplex.com 正在完善,希望有兴趣的朋友关注。好了以后会在这篇文章中告诉大家。

广告时间:

看到快乐组织技术沙龙,我热血澎湃的。表示支持。哈哈!!!!!!

广告
正在准备一次北京的技术沙龙活动,目前正在筹备阶段,有兴趣的朋友可以报名,规格在20到50人之间。讨论内容范围待定。以交友为主,技术会友!QQ群:51825601(北京.NET之友技术沙龙)

备注:此群主要用于组织沙龙活动,仅限北京

欢迎各位朋友捧场!O(∩_∩)O哈哈~

配置:

需要修改的内容,此处省略1000个字。

数据库:/Files/winner2009/Widget_Bak.rar

源码: /Files/winner2009/Widget.rar

Demo: http://xiaoguan1985.36jhw.dnscnc.com/

不好意思,刚传错了一个文件,请大家重新下载源码。

[转载]Visual Studio使用小技巧2 – 使用任务列表(task list)

mikel阅读(868)

[转载]Visual Studio使用小技巧2 – 使用任务列表(task list) – 秋心的Blog – 博客园.

大家一定都很熟悉错误列表(error list),当我们写的语句有语法错误时,错误信息会列在错误列表(error list)中.双击某个错误,Visual Studio会自动帮我们定位到源代码中相应的地方。

任务列表(task list)是错误列表(error list)的孪生兄弟。我们可以通过View –> Task List 打开它。有两种类型的任务: 用户任务(user task)和注释(comment),在任务列表(task list)上部的下拉列表中可以选中查看哪种类型。

用户任务(user task): 任务列表上部有个Create User Task的按钮。点击它就可以创建新的用户任务。用户任务有优先级,是否完成,描述这3个属性(参考下图)。

image

警告: 用户任务保存在隐藏的solution文件(.suo)中,因此它很容易被破坏。

注释(comment): 我想我们都有过这种经历:写了些测试代码, 然后在注释中写上//todelete: ***。任务完成后全文搜索todelete,然后删除测试代码。

现在Visual Studio可以帮更容易的定位到某些以特殊标签开头的注释。例如 //TODO:

//TODO:  to remove the test code.

在任务列表中会显示成下面的样子

image

如果想在源代码中定位到注释的地方,简单的双击即可。默认有三个标签 HACK, TODO 和 UNDONE. 你可以加入你自己的标签。Tools –> Options, 找到Environment –> Task List,在这里你可以增加,修改和删除标签,下图中MyTag标签是新增加的。

image