使用FlexiGrid实现Extjs表格效果方法分享


Posted in Javascript onDecember 16, 2014

近一段时间Extjs真的是风光无限好,只要是个做CRM/HRM之类的企业现在都在琢磨怎么在项目中用它,不过兄弟我可是不敢,原因很简单:太大/太笨/源码不好调试。但是对于Extjs漂亮的表格与功能的强大,实在是让我垂涎三尺,记得以前有个老外同志写过一个类似的Extjs的Jquery插件,所以就在Jquery的插件海洋中一顿海找,呵呵,还真让我找到了。看来还是我的Jquery好,小巧简单好像一部好的汽车引擎,我想要什么就可以自已DIY,真是方便。总体方案在网络传输上不超过80KB,速度比500KB大小的Extjs不知道要小上多少哪。。。

使用FlexiGrid实现Extjs表格效果方法分享

使用FlexiGrid实现Extjs表格效果方法分享

下载地址:http://code.google.com/p/flexigrid/

不过由于FlexiGrid在互联网上的大部分资料都是用PHP或者java写的,所以兄弟简单的对它进行了改制,也做了一个山寨版的Extjs表格实现,希望对大家有帮助。

基本使用:

使用FlexiGrid实现Extjs表格效果方法分享

1 基本使用是非常简单的,只需要加入Jquery库与FlexiGrid的JS即可以对表格进行格式化与美化.

<link rel="stylesheet" type="text/css" href="css/flexigrid/flexigrid.css">

<script type="text/javascript" src="lib/jquery/jquery-1.2.6.min.js"></script>

<script type="text/javascript" src="flexigrid.pack.js"></script>

<%--<script type="text/javascript" src="lib/jquery/jquery-1.2.6-vsdoc-cn.js"></script>--%>

<script type="text/javascript">

    $("document").ready(function() {

    $('#flexme1').flexigrid();

    $('#flexme2').flexigrid();

    });

</script>

2 加入需要格式化的表格即可

<h1>

     最简单的flexigrid表格-1</h1>

 <table id="flexme1">

     <thead>

         <tr>

             <th width="100">

                 Col 1

             </th>

             <th width="100">

                 Col 2

             </th>

             <th width="100">

                 Col 3 is a long header name

             </th>

             <th width="300">

                 Col 4

             </th>

         </tr>

     </thead>

     <tbody>

         <tr>

             <td>

                 This is data 1 with overflowing content

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

     </tbody>

 </table>

 <p>

 </p>

 <h1>

     最简单的flexigrid表格-2</h1>

 <table id="flexme2">

     <thead>

         <tr>

             <th width="100">

                 Col 1

             </th>

             <th width="100">

                 Col 2

             </th>

             <th width="100">

                 Col 3 is a long header name

             </th>

             <th width="300">

                 Col 4

             </th>

         </tr>

     </thead>

     <tbody>

         <tr>

             <td>

                 This is data 1 with overflowing content

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

         <tr>

             <td>

                 This is data 1

             </td>

             <td>

                 This is data 2

             </td>

             <td>

                 This is data 3

             </td>

             <td>

                 This is data 4

             </td>

         </tr>

     </tbody>

 </table>

为了增加FlexiGrid的基本使用效果,我们可以通过参数对其进行基本的调整

自定义表头

使用FlexiGrid实现Extjs表格效果方法分享

具体代码实现:

<script type="text/javascript">

     $("document").ready(function() {

         $('#flexme1').flexigrid({

             colModel: [

             { display: 'ISO', name: 'iso', width: 40, sortable: true, align: 'center' },

             { display: 'Name', name: 'name', width: 180, sortable: true, align: 'left' },

             { display: 'Printable Name', name: 'printable_name', width: 120, sortable: true, align: 'left' },

             { display: 'ISO3', name: 'iso3', width: 130, sortable: true, align: 'left', hide: true },

             { display: 'Number Code', name: 'numcode', width: 80, sortable: true, align: 'right' }

             ]

         });

         $('#flexme2').flexigrid({

             colModel: [

             { display: 'ISO', name: 'iso', width: 40, sortable: true, align: 'center' },

             { display: 'Name', name: 'name', width: 180, sortable: true, align: 'left' },

             { display: 'Printable Name', name: 'printable_name', width: 120, sortable: true, align: 'left' },

             { display: 'ISO3', name: 'iso3', width: 130, sortable: true, align: 'left', hide: true },

             { display: 'Number Code', name: 'numcode', width: 80, sortable: true, align: 'right' }

             ],

             sortname: "iso",

             sortorder: "asc",

         });

     });

 </script>

自定义折叠,自定义排序的实现

使用FlexiGrid实现Extjs表格效果方法分享

<script type="text/javascript">

     $("document").ready(function() {

         $('#flexme1').flexigrid({

             colModel: [

             { display: 'ISO', name: 'iso', width: 40, sortable: true, align: 'center' },

             { display: 'Name', name: 'name', width: 180, sortable: true, align: 'left' },

             { display: 'Printable Name', name: 'printable_name', width: 120, sortable: true, align: 'left' },

             { display: 'ISO3', name: 'iso3', width: 130, sortable: true, align: 'left', hide: true },

             { display: 'Number Code', name: 'numcode', width: 80, sortable: true, align: 'right' }

             ], width: 700, height: 300, usepager: true, showTableToggleBtn: true, title: "点我折叠"

         });

         $('#flexme2').flexigrid({

             colModel: [

             { display: 'ISO', name: 'iso', width: 40, sortable: true, align: 'center' },

             { display: 'Name', name: 'name', width: 180, sortable: true, align: 'left' },

             { display: 'Printable Name', name: 'printable_name', width: 120, sortable: true, align: 'left' },

             { display: 'ISO3', name: 'iso3', width: 130, sortable: true, align: 'left', hide: true },

             { display: 'Number Code', name: 'numcode', width: 80, sortable: true, align: 'right' }

             ],

             searchitems: [

             { display: 'ISO', name: 'iso' },

             { display: 'Name', name: 'name', isdefault: true }

             ],

             sortname: "iso",

             sortorder: "asc",

             title: "我的测试效果",

             width: 700,

             height: 300,

             usepager: true, showTableToggleBtn: true, rp: 10

  

         });

     });

 </script>

高级使用:

使用FlexiGrid实现Extjs表格效果方法分享

1 分页用到的存储过程

Create PROCEDURE [dbo].[spAll_ReturnRows]

        (

            @SQL nVARCHAR(4000),

            @Page int,

            @RecsPerPage int,

            @ID VARCHAR(255),

            @Sort VARCHAR(255)

        )

        AS

        DECLARE @Str nVARCHAR(4000)

        SET @Str='SELECT   TOP '+

            CAST(@RecsPerPage AS VARCHAR(20))+

            ' * FROM ('+@SQL+') T WHERE T.'+

            @ID+

            ' NOT IN (SELECT   TOP '+

            CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+

            ' '+

            @ID+

            ' FROM ('

            +@SQL+

            ') T9 ORDER BY '+

            @Sort+

            ') ORDER BY '+

            @Sort

        PRINT @Str

        EXEC sp_ExecuteSql @Str

2 异步JSON数据传输实现

 using System;

 using System.Collections.Generic;

 using System.Configuration;

 using System.Data;

 using System.Data.SqlClient;

 using System.Linq;

 using System.Text;

 using System.Web;

 using System.Web.Services;

 using Newtonsoft.Json;

 namespace GridDemo

 {

     /// <summary>

     /// $codebehindclassname$ 的摘要说明

     /// </summary>

     [WebService(Namespace = "http://tempuri.org/")]

     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

     public class GetDataSource4 : IHttpHandler

     {

         public void ProcessRequest(HttpContext context)

         {

             context.Response.ContentType = "text/plain";

             //得到当前页

             string CurrentPage = context.Request["page"];

             //得到每页显示多少

             string PageShowLimit = context.Request["rp"];

             //得到主键

             string TableID = context.Request["sortname"];

             //得到排序方法

             string OrderMethod = context.Request["sortorder"];

             //得到要过滤的字段

             string FilterField = context.Request["qtype"];

             //得到要过滤的内容

             string FilterFieldContext;

             if (context.Request.Form["letter_pressed"] == null)

             {

                 FilterFieldContext = "";

             }

             else

             {

                 FilterFieldContext = context.Request["letter_pressed"];

             }

             //得到表的总行数

             string TableRowCount = SqlHelper.ExecuteScalar(ConfigurationManager.AppSettings["SQL2"],

                                     CommandType.Text,

                                     "select count(*) from Person.Address"

                                    ).ToString();

             //得到主SQL

             SqlParameter SQL = new SqlParameter("@SQL", SqlDbType.NVarChar);

             //SQL.Value = "SELECT  * FROM Person.Address";

             if (FilterFieldContext.Length == 0 || FilterField.Length == 0)

             {

                 SQL.Value = "SELECT  AddressID,AddressLine1,AddressLine2,PostalCode,City FROM Person.Address";

             }

             else

             {

                 string[] tmp = FilterField.Split(',');

                 SQL.Value = "SELECT  AddressID,AddressLine1,AddressLine2,PostalCode,City FROM Person.Address where " + tmp[0] + " like '" + FilterFieldContext + "%'";

             }

             SqlParameter Page = new SqlParameter("@Page", SqlDbType.Int);

             Page.Value = Convert.ToInt32(CurrentPage);

             SqlParameter RecsPerPage = new SqlParameter("@RecsPerPage", SqlDbType.Int);

             RecsPerPage.Value = Convert.ToInt32(PageShowLimit);

             SqlParameter ID = new SqlParameter("@ID", SqlDbType.VarChar);

             ID.Value = TableID;

             SqlParameter Sort = new SqlParameter("@Sort", SqlDbType.VarChar);

             Sort.Value = TableID;

             //得到表

             DataTable returnTable = SqlHelper.ExecuteDataset(ConfigurationManager.AppSettings["SQL2"],

                         CommandType.StoredProcedure, "spAll_ReturnRows",

                         new SqlParameter[]

                             {

                                 SQL,Page,RecsPerPage,ID,Sort

                             }).Tables[0];

             context.Response.Write(DtToSON2(returnTable, CurrentPage, TableRowCount));

         }

         /// <summary>

         /// JSON格式转换

         /// </summary>

         /// <param name="dt">DataTable表</param>

         /// <param name="page">当前页</param>

         /// <param name="total">总计多少行</param>

         /// <returns></returns>

         public static string DtToSON2(DataTable dt, string page, string total)

         {

             StringBuilder jsonString = new StringBuilder();

             jsonString.AppendLine("{");

             jsonString.AppendFormat("page: {0},\n", page);

             jsonString.AppendFormat("total: {0},\n", total);

             jsonString.AppendLine("rows: [");

             for (int i = 0; i < dt.Rows.Count; i++)

             {

                 jsonString.Append("{");

                 jsonString.AppendFormat("id:'{0}',cell:[", dt.Rows[i][0].ToString());

                 for (int j = 0; j < dt.Columns.Count; j++)

                 {

                     if (j == dt.Columns.Count - 1)

                     {

                         jsonString.AppendFormat("'{0}'", dt.Rows[i][j].ToString());

                     }

                     else

                     {

                         jsonString.AppendFormat("'{0}',", dt.Rows[i][j].ToString());

                     }

                     if (j == dt.Columns.Count - 1)

                     {

                         jsonString.AppendFormat(",'{0}'", "<input type=\"button\" value=\"查看\" id=\"sss\" onclick=\"sss(" + dt.Rows[i][0].ToString() + ")\" />");

                     }

                 }

                 jsonString.Append("]");

                 if (i == dt.Rows.Count - 1)

                 {

                     jsonString.AppendLine("}");

                 }

                 else

                 {

                     jsonString.AppendLine("},");

                 }

             }

             jsonString.Append("]");

             jsonString.AppendLine("}");

             return jsonString.ToString();

         }

         public bool IsReusable

         {

             get

             {

                 return false;

             }

         }

     }

 }

3 页面实现

 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Complex-8.aspx.cs" Inherits="GridDemo.Complex_8" %>

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 <html xmlns="http://www.w3.org/1999/xhtml">

 <head runat="server">

     <title></title>

     <link rel="stylesheet" type="text/css" href="/css/flexigrid/flexigrid.css" />

     <script type="text/javascript" src="/lib/jquery/jquery.js"></script>

     <script type="text/javascript" src="flexigrid.js"></script>

     <link type="text/css" rel="Stylesheet" href="facebox/facebox.css" />

     <link type="text/css" rel="Stylesheet" href="body.css" />

     <script type="text/javascript" src="facebox/facebox.js"></script>

     <script type="text/javascript">

         $("document").ready(function() {

             $("#flex1").flexigrid

             ({

                 url: 'GetDataSource4.ashx',

                 dataType: 'json',

                 colModel: [

                 { display: '地址ID', name: 'AddressID', width: 40, sortable: true, align: 'center' },

                 { display: '具体地址1', name: 'AddressLine1', width: 140, sortable: true, align: 'left' },

                 { display: '具体地址2', name: 'AddressLine2', width: 80, sortable: true, align: 'left' },

                 { display: '邮编', name: 'PostalCode', width: 80, sortable: true, align: 'left' },

                 { display: '城市', name: 'City', width: 80, sortable: true, align: 'left' },

                 { display: '操作', name: 'Opt', width: 80, sortable: true, align: 'left' }

                 ],

                 buttons: [

                 { name: 'A', onpress: sortAlpha },

                 { name: 'B', onpress: sortAlpha },

                 { name: 'C', onpress: sortAlpha },

                 { name: 'D', onpress: sortAlpha },

                 { name: 'E', onpress: sortAlpha },

                 { name: 'F', onpress: sortAlpha },

                 { name: 'G', onpress: sortAlpha },

                 { name: 'H', onpress: sortAlpha },

                 { name: 'I', onpress: sortAlpha },

                 { name: 'J', onpress: sortAlpha },

                 { name: 'K', onpress: sortAlpha },

                 { name: 'L', onpress: sortAlpha },

                 { name: 'M', onpress: sortAlpha },

                 { name: 'N', onpress: sortAlpha },

                 { name: 'O', onpress: sortAlpha },

                 { name: 'P', onpress: sortAlpha },

                 { name: 'Q', onpress: sortAlpha },

                 { name: 'R', onpress: sortAlpha },

                 { name: 'S', onpress: sortAlpha },

                 { name: 'T', onpress: sortAlpha },

                 { name: 'U', onpress: sortAlpha },

                 { name: 'V', onpress: sortAlpha },

                 { name: 'W', onpress: sortAlpha },

                 { name: 'X', onpress: sortAlpha },

                 { name: 'Y', onpress: sortAlpha },

                 { name: 'Z', onpress: sortAlpha },

                 { name: '%', onpress: sortAlpha }

                 ],

                 searchitems: [

                  { display: '城市', name: 'City' , isdefault: true},

                  { display: '邮编', name: 'PostalCode' }

                  ],

                 usepager: true,

                 title: '客户信息',

                 useRp: true,

                 rp: 10,

                 showTableToggleBtn: true,

                 width: 700,

                 height: 200,

                 rpOptions: [10, 15, 20, 25, 40, 60], //可选择设定的每页结果数

                 procmsg: '请等待数据正在加载中 …', //正在处理的提示信息

                 resizable: false, //是否可伸缩

                 sortname: "AddressID",

                 //sortorder: "asc",//此列由于存储过程原因无法用

             });

         });

         function sortAlpha(com) {

             jQuery('#flex1').flexOptions({ newp: 1, params: [{ name: 'letter_pressed', value: com }, { name: 'qtype', value: $('select[name=qtype]').val()}] });

             jQuery("#flex1").flexReload();

         }

         function sss(data)

         {

            var temp=eval(data);

 //           jQuery.facebox(temp);

            jQuery.facebox({ ajax: 'Default.aspx?id='+temp })

         }

     </script>

 </head>

 <body>

     <table>

     </table>

 </body>

 </html>
Javascript 相关文章推荐
javascript 数据类型转换(parseInt,parseFloat)
Jul 20 Javascript
jquery单选框radio绑定click事件实现方法
Jan 14 Javascript
jQuery Easyui Datagrid实现单行的上移下移及保存移动的结果
Aug 15 Javascript
利用jquery获取select下拉框的值
Nov 23 Javascript
微信小程序 五星评分的实现实例
Aug 04 Javascript
浅析node.js的模块加载机制
May 25 Javascript
微信小程序使用wxParse解析html的方法教程
Jul 06 Javascript
微信小程序实现底部导航
Nov 05 Javascript
vue-cli项目使用mock数据的方法(借助express)
Apr 15 Javascript
深入了解响应式React Native Echarts组件
May 29 Javascript
如何在postman中添加cookie信息步骤解析
Jun 30 Javascript
JavaScript事件委托实现原理及优点进行
Aug 29 Javascript
jQuery+css实现百度百科的页面导航效果
Dec 16 #Javascript
jQuery+PHP打造滑动开关效果
Dec 16 #Javascript
javascript进行四舍五入方法汇总
Dec 16 #Javascript
javascript 判断整数方法分享
Dec 16 #Javascript
使用jQuery不判断浏览器高度解决iframe自适应高度问题
Dec 16 #Javascript
jquery队列函数用法实例
Dec 16 #Javascript
JQuery仿小米手机抢购页面倒计时效果
Dec 16 #Javascript
You might like
用php和MySql来与ODBC数据连接
2006/10/09 PHP
PHP处理JSON字符串key缺少双引号的解决方法
2014/09/16 PHP
PHP基于socket实现客户端和服务端通讯功能
2017/07/13 PHP
jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理
2015/11/24 Javascript
ros::spin() 和 ros::spinOnce()函数的区别及详解
2016/10/01 Javascript
AngularJS入门教程之数据绑定原理详解
2016/11/02 Javascript
详解nodejs中的process进程
2017/03/19 NodeJs
vue实现图书管理demo详解
2017/10/17 Javascript
JS动画定时器知识总结
2018/03/23 Javascript
浅析Vue 生命周期
2018/06/21 Javascript
jquery实现的简单轮播图功能【适合新手】
2018/08/17 jQuery
vue项目中使用tinymce编辑器的步骤详解
2018/09/11 Javascript
Electron 如何调用本地模块的方法
2019/02/01 Javascript
Vue 集成 PDF.js 实现 PDF 预览和添加水印的步骤
2021/01/22 Vue.js
[14:25]教你分分钟做大人:主宰(HEROS)
2014/12/08 DOTA
python 遍历列表提取下标和值的实例
2018/12/25 Python
Python实现监控Nginx配置文件的不同并发送邮件报警功能示例
2019/02/26 Python
基于python的socket实现单机五子棋到双人对战
2020/03/24 Python
Python hashlib加密模块常用方法解析
2019/12/18 Python
Python使用循环神经网络解决文本分类问题的方法详解
2020/01/16 Python
tensorflow入门:TFRecordDataset变长数据的batch读取详解
2020/01/20 Python
Python基于pillow库实现生成图片水印
2020/09/14 Python
pandas 按日期范围筛选数据的实现
2021/02/20 Python
凯普林包包西班牙官网:Kipling西班牙
2019/04/12 全球购物
印尼购物网站:iLOTTE
2019/10/16 全球购物
Ben Sherman官方网站:英国男装品牌
2019/10/22 全球购物
互联网电子商务专业毕业生求职信
2014/03/18 职场文书
乡镇纠风工作实施方案
2014/03/22 职场文书
抵押贷款承诺书
2014/05/30 职场文书
检查机关党的群众路线个人整改措施
2014/10/04 职场文书
机关班子查摆问题及整改措施
2014/10/28 职场文书
检讨书大全
2015/01/27 职场文书
本科毕业答辩开场白
2015/05/27 职场文书
2019安全宣传标语大全
2019/08/14 职场文书
javascript拖曳互换div的位置实现示例
2021/06/28 Javascript
css3应用示例:新增的选择器
2022/03/16 HTML / CSS