一中2009音乐征集系统 推荐平台

3

Comments

经过约两周的努力编写和调试,终于在昨天晚上完成了。因为做这个网站的缘故,再次新学习了大量东西,比如 jQuery,以及 Linux 下 Photoshop 的替代品 GIMP 等。同时也权当是一次练手,毕竟很久没写这种东西了。不过这次又一次与 IE 的 bug 及非标准化进行了顽强的斗争……

我要记录下在这个过程中遇到的种种问题和解决方法,以后再遇到就有地方查了~

客户端部分

很早就听说了 jQuery,上次看完 wp-codebox 的代码才顿觉 jQuery 的强大,因此这次选择了这个。经过短暂的学习就基本了解了,还是比较简单的。此外,jQuery 的插件库相当强大,里面找到了所有我需要的东西,包括上传文件的插件和播放器的插件等。

编写客户端代码最艰苦的部分就是配色和设计界面,以及在各个浏览器中调试。关于兼容性的问题放在后面说,前面先写点关于 JavaScript 代码的事。

第一个比较有趣的问题是关于浮点数保留精度的问题。虽然保留精度大是件好事,可是 JavaScript 没有如 C 中的 printf 那么方便的东西(不过网上似乎也有类似实现,不过 JavaScript 实现这个东西……还是算了吧……)。而一下把很长很长的浮点数显示在用户面前,也绝对不是件好事。经过搜寻,最后使用下面代码实现:

1
2
3
4
5
6
Number.prototype.fix = function (num) {
  with (Math) return round(this.valueOf() * pow(10, num)) / pow(10, num);
};
String.prototype.fix = function (num) {
  return parseFloat(this).fix(num);
};

第二个问题就是关于 MP3 播放器背景的问题。我用的这个播放器插件极具可定制性,背景色和前景色都能定制,不过要求都要使用十六进制的形式传入。为了更好的适应主题颜色,我就使用 jQuery 的 .css 方法在脚本中现场获取当前在 CSS 中定义的前景色和背景色。结果发现,其在 Firefox 内返回的是类似 rgb(0, 0, 0) 这样的颜色表大形式。据说在 Firefox 中永远放回这种形式,而 IE 中则总是返回你实际设置时使用的格式。针对这个问题,我写了下面代码来解决(毋庸置疑,这又是抄的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var hex = function (n) {
  if (n == null) return '00';
  n = parseInt(n);
  if (n == 0 || isNaN(n)) return '00';
  n = Math.round(n);
  if (n < 0) n = 0;
  else if (n > 255) n = 255;
  return '0123456789abcdef'.charAt((n - n % 16) / 16) +
         '0123456789abcdef'.charAt(n % 16);
};
var color2hex = function (color) {
  if (color.search(/rgb/i) > -1) {
    color = color.substring(4, color.length - 1).split(', ');
    return hex(color[0]) + hex(color[1]) + hex(color[2]);
  } else if (color.charAt(0) == '#') {
    color = color.substring(1);
    if (color.length == 3) {
      return color.charAt(0) + color.charAt(0) +
             color.charAt(1) + color.charAt(1) +
             color.charAt(2) + color.charAt(2);
    } else {
      return color;
    }
  }
};

另外就是,为了防止大家大量重复提交同样音乐,这次加入了搜索功能,而且在提交一个音乐的时候就会自动进行搜索,并提示用户检查是否已有相同曲目被提交。至于搜索的实现,采用的是客户端搜索,算法上采用了非常著名而简单的 LCS (Longest common subsequence, 最长公共子序列) 这一 oier 的居家旅行杀人放火必备算法。事实证明,这一算法其实是很有效的~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function lcs(str1, str2) {
  if (! str1 || ! str2) return 0;
  var arr = [];
  var i, j;
  for (i = 0; i < str1.length; ++i) {
    arr[i] = [];
    for (j = 0; j < str2.length; ++j) {
      arr[i][j] = Math.max(i ? arr[i-1][j] : 0, j ? arr[i][j-1] : 0);
      if (str1.charAt(i) == str2.charAt(j))
        arr[i][j] = Math.max(arr[i][j], i && j ? arr[i-1][j-1] + 1 : 1);
    }
  }
  return arr[str1.length-1][str2.length-1];
}

搜索算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function search(type, text, callback) {
  text = text.replace(/\s+/g, '').toLowerCase();
  var threshold = text.length * 0.25;
  var result = [];
  getList(type, function (value) {
    if (! value || typeof value == 'number') {
      callback(value);
      return;
    }
    var itemtext, item, i;
    for (i = 0; i < value.length; ++i) {
      item = value[i];
      itemtext = (item.source + item.title).replace(/\s+/g, '').toLowerCase();
      item.len = itemtext.length;
      item.lcs = lcs(text, itemtext);
      if (item.lcs > threshold) result.push(item);
    }
    result.sort(function (item1, item2) {
      if (item1.lcs > item2.lcs) return -1;
      if (item1.lcs < item2.lcs) return 1;
      if (item1.len < item2.len) return -1;
      if (item1.len > item2.len) return 1;
      return 0;
    });
    callback(result);
  });
}

还有一个是关于换肤的。上一次有一篇文章测试过了基于更换 link 标签 href 属性换肤的可行性分析,这次就应用于了这个平台上。但在测试中发现 IE 并不像想像的那么完美。事实上,IE 在这个的测试过程中是非常废品的,几乎相当于不能换。经过多次实验,发现原因出在——IE6 在页面加载完毕后永远不会显示其还没有下载的样式信息。也就是说,更改 href 后,虽然 IE6 将原来的样式表无效化了,但此时发现新的样式表不在其缓存中,便拒绝启用新的样式表。但奇怪的是,IE6 这个时候事实上会连接服务器下载新的样式,因此在下一次访问的时候上次点过的皮肤就可以用了。这说明,我们只要要求 IE 预装载以后可能用到的东西就可以了。因此我就在页面底部加入了一个隐藏的 iframe 用于下载样式表和需要的图片。

除此之外,就是一些关于样式的新发现。我第一次知道当一个元素的 position 属性设置为 relative 的时候,对这个对象本身没有影响,但却使得其使用 position 属性的子元素的位移变由相对页面的位移变为相对该元素的位移。这的上传进度条的实现方法便是应用这一机制。

说到上传进度条,不得不说 Uploadify 这一插件真的很强大……我将其默认的上传进度和管理通过对各个必要函数绑定返回 false 来屏蔽掉了。

最后是关于 jQuery 优化的问题。虽然 jQuery 的官方测试表明,jQuery 的效率非常惊人,但优化下总归是有好处的。参考的资料见下面了,说说我大体做了哪些优化吧。一是缓存 jQuery 对象,防止重复调用选择器带来的性能损失。第二个是为选择器加上上下文限制,第三个是将部分效果移至 $(window).load 里。

在客户端的脚本部分的最最后,小小的埋怨一下 JavaScript 这个语言……我觉得实在是没有 Python 优美啊!什么时候 Python 能成为通用的客户端脚本啊,那个时候用 Python 就可以通吃服务器端和客户端了……

说完了脚本,来说说这次的界面。这次界面个人还是很满意的。在界面设计中,我自认为值得一提的是那个圆角,为什么呢?因为我以前一直以为 GIMP 不能画圆角。这次去网上找了才知道,GIMP 远比我想象的强大的多!猜猜圆角是怎么画出来的?是通过高斯模糊+调整色阶。具体的实现方法放在下面参考资料里面了,这里就不详述了。但我真的觉得很强大,虽然 GIMP 和 Photoshop 等商业软件还有差距,不过对于我来说,看来足够了。

最后再提下这次发现的不同浏览器的兼容性问题:

对于不显示的 Flash,IE 无法与其交互
对于曾经被隐藏但再次出现的 Flash,IE6 无法与其交互
解决方案:完全销毁原有 Flash 对象并重建
IE 中隐藏的 Flash MP3 播放器可以发出声音,但 Firefox 不行
解决方案:统一的办法就是直接销毁播放器,而非单单让其隐藏
IE 的 JavaScript 解析器不支持用 [index] 的方式索引字符
解决方案:使用原有标准的 .charAt(index) 方式索引
在 IE6 中,显示时尚未下载完成的 CSS 元素永远不会显示(如背景图片)
解决方案:在页面中加入一个隐藏的 iframe 用于预加载
IE6 中的双倍外边距 bug
解决方案:给相应对象加上 display:inline; 属性,或通过 hack 专门调低在 IE6 下的外边距
IE6 对于 display 的 inline-block 属性支持不完善
解决方案:给相应对象加上 zoom:1; 属性
IE6 不支持半透明的 png 图片
解决方案:采用 IE 专有的过滤器 filter:progid:DXImageTransform.Microsoft.AlphaImageLoader 可以完美解决

IE 的兼容性烦死人啊……看看我的 ie6-fix.css 和 ie7-fix.css 就知道了……

使用部件

参考资料

Pages: 1 2

3 Responses to “一中2009音乐征集系统 推荐平台”

  1. tank Says:
    2009年11月12日 02:09 回复

    看的头晕了…

  2. Robot Says:
    2009年11月13日 10:45 回复

    已经在帖吧里置顶了……

    看起来反响还是不错的,对平台,以及对活动……

    不过,我一直担心这个上传功能……要是有人吃饱撑着传N个20M的MP3上去,把服务器硬盘撑爆,或者把数据库拖跨……

    虽然单机很难做到,但是多台机子用脚本同时上传的话……这莫非就是DDOS了……

  3. 狗狗 Says:
    2009年11月29日 23:20 回复

    那个…那个…你的主页在opera mini上的显示很有问题……

Leave a Reply